Commit dbb85690e5ee7e65af6e972722a8faf760fd547f
1 parent
6c0755f1
feat: device profile add card mode
Showing
10 changed files
with
764 additions
and
311 deletions
| @@ -2,7 +2,9 @@ import { defHttp } from '/@/utils/http/axios'; | @@ -2,7 +2,9 @@ import { defHttp } from '/@/utils/http/axios'; | ||
| 2 | import { | 2 | import { |
| 3 | TDeviceConfigParams, | 3 | TDeviceConfigParams, |
| 4 | IDeviceConfigAddOrEditModel, | 4 | IDeviceConfigAddOrEditModel, |
| 5 | + ProfileRecord, | ||
| 5 | } from '/@/api/device/model/deviceConfigModel'; | 6 | } from '/@/api/device/model/deviceConfigModel'; |
| 7 | +import { PaginationResult } from '/#/axios'; | ||
| 6 | 8 | ||
| 7 | enum EDeviceConfigApi { | 9 | enum EDeviceConfigApi { |
| 8 | /** | 10 | /** |
| @@ -52,7 +54,7 @@ export const alarmContactGetPage = () => { | @@ -52,7 +54,7 @@ export const alarmContactGetPage = () => { | ||
| 52 | * 分页查询设备配置页面 | 54 | * 分页查询设备配置页面 |
| 53 | */ | 55 | */ |
| 54 | export const deviceConfigGetQuery = (params?: TDeviceConfigParams) => { | 56 | export const deviceConfigGetQuery = (params?: TDeviceConfigParams) => { |
| 55 | - return defHttp.get({ | 57 | + return defHttp.get<PaginationResult<ProfileRecord>>({ |
| 56 | url: EDeviceConfigApi.DEVICE_CONFIG_GET_PAGE, | 58 | url: EDeviceConfigApi.DEVICE_CONFIG_GET_PAGE, |
| 57 | params, | 59 | params, |
| 58 | }); | 60 | }); |
| @@ -199,3 +199,45 @@ export interface AlarmLogItem { | @@ -199,3 +199,45 @@ export interface AlarmLogItem { | ||
| 199 | organizationId: string; | 199 | organizationId: string; |
| 200 | organizationName: string; | 200 | organizationName: string; |
| 201 | } | 201 | } |
| 202 | + | ||
| 203 | +export interface Configuration { | ||
| 204 | + type: string; | ||
| 205 | +} | ||
| 206 | + | ||
| 207 | +export interface TransportConfiguration { | ||
| 208 | + type: string; | ||
| 209 | +} | ||
| 210 | + | ||
| 211 | +export interface ProvisionConfiguration { | ||
| 212 | + type: string; | ||
| 213 | + provisionDeviceSecret?: any; | ||
| 214 | +} | ||
| 215 | + | ||
| 216 | +export interface ProfileData { | ||
| 217 | + configuration: Configuration; | ||
| 218 | + transportConfiguration: TransportConfiguration; | ||
| 219 | + provisionConfiguration: ProvisionConfiguration; | ||
| 220 | + alarms?: any; | ||
| 221 | +} | ||
| 222 | + | ||
| 223 | +export interface ProfileRecord { | ||
| 224 | + id: string; | ||
| 225 | + creator: string; | ||
| 226 | + createTime: string; | ||
| 227 | + updater: string; | ||
| 228 | + updateTime: string; | ||
| 229 | + name: string; | ||
| 230 | + tenantId: string; | ||
| 231 | + transportType: string; | ||
| 232 | + provisionType: string; | ||
| 233 | + deviceType: string; | ||
| 234 | + tbProfileId: string; | ||
| 235 | + profileData: ProfileData; | ||
| 236 | + defaultRuleChainId: string; | ||
| 237 | + defaultQueueName: string; | ||
| 238 | + image: string; | ||
| 239 | + type: string; | ||
| 240 | + default: boolean; | ||
| 241 | + | ||
| 242 | + checked?: boolean; | ||
| 243 | +} |
src/components/Widget/CardLayoutButton.vue
0 → 100644
| 1 | +<script lang="ts" setup> | ||
| 2 | + import { Popover, Slider, Button } from 'ant-design-vue'; | ||
| 3 | + import { LayoutOutlined } from '@ant-design/icons-vue'; | ||
| 4 | + import { computed } from 'vue'; | ||
| 5 | + | ||
| 6 | + const props = withDefaults( | ||
| 7 | + defineProps<{ | ||
| 8 | + min?: number; | ||
| 9 | + max?: number; | ||
| 10 | + value?: number; | ||
| 11 | + }>(), | ||
| 12 | + { | ||
| 13 | + min: 4, | ||
| 14 | + max: 12, | ||
| 15 | + value: 4, | ||
| 16 | + } | ||
| 17 | + ); | ||
| 18 | + | ||
| 19 | + const emit = defineEmits(['change', 'update:value']); | ||
| 20 | + | ||
| 21 | + const generateLayoutMarks = (min: number, max: number) => { | ||
| 22 | + const marks = {}; | ||
| 23 | + Array.from({ length: max - min + 1 }).forEach((_, index) => { | ||
| 24 | + const key = index + min; | ||
| 25 | + marks[key] = key; | ||
| 26 | + }); | ||
| 27 | + return marks; | ||
| 28 | + }; | ||
| 29 | + | ||
| 30 | + const getMarks = computed(() => { | ||
| 31 | + const { min, max } = props; | ||
| 32 | + return generateLayoutMarks(min, max); | ||
| 33 | + }); | ||
| 34 | + | ||
| 35 | + const handleChange = (value: number) => { | ||
| 36 | + emit('update:value', value); | ||
| 37 | + emit('change', value); | ||
| 38 | + }; | ||
| 39 | +</script> | ||
| 40 | + | ||
| 41 | +<template> | ||
| 42 | + <Popover :trigger="['hover']"> | ||
| 43 | + <template #content> | ||
| 44 | + <div class="w-60"> | ||
| 45 | + <div>每行显示数量</div> | ||
| 46 | + <Slider | ||
| 47 | + :value="props.value" | ||
| 48 | + :max="props.max" | ||
| 49 | + :min="props.min" | ||
| 50 | + :marks="getMarks" | ||
| 51 | + @change="handleChange" | ||
| 52 | + /> | ||
| 53 | + </div> | ||
| 54 | + </template> | ||
| 55 | + <Button type="primary"> | ||
| 56 | + <LayoutOutlined /> | ||
| 57 | + </Button> | ||
| 58 | + </Popover> | ||
| 59 | +</template> |
src/components/Widget/ModeSwitchButton.vue
0 → 100644
| 1 | +<script lang="ts" setup> | ||
| 2 | + import { RadioButton, RadioGroup, Tooltip } from 'ant-design-vue'; | ||
| 3 | + import { AppstoreOutlined, UnorderedListOutlined } from '@ant-design/icons-vue'; | ||
| 4 | + import { Mode } from './const'; | ||
| 5 | + const props = defineProps<{ | ||
| 6 | + value: Mode; | ||
| 7 | + }>(); | ||
| 8 | + | ||
| 9 | + const emit = defineEmits(['change']); | ||
| 10 | + | ||
| 11 | + const handleChange = (event: Event) => { | ||
| 12 | + const value = (event.target as HTMLInputElement).value; | ||
| 13 | + emit('change', value); | ||
| 14 | + }; | ||
| 15 | +</script> | ||
| 16 | + | ||
| 17 | +<template> | ||
| 18 | + <RadioGroup :value="props.value" button-style="solid" @change="handleChange"> | ||
| 19 | + <Tooltip title="卡片模式"> | ||
| 20 | + <RadioButton class="cursor-pointer" :value="Mode.CARD"> | ||
| 21 | + <AppstoreOutlined /> | ||
| 22 | + </RadioButton> | ||
| 23 | + </Tooltip> | ||
| 24 | + <Tooltip title="列表模式"> | ||
| 25 | + <RadioButton class="cursor-pointer" :value="Mode.TABLE"> | ||
| 26 | + <UnorderedListOutlined /> | ||
| 27 | + </RadioButton> | ||
| 28 | + </Tooltip> | ||
| 29 | + </RadioGroup> | ||
| 30 | +</template> |
src/components/Widget/const.ts
0 → 100644
src/components/Widget/index.ts
0 → 100644
src/views/device/profiles/CardMode.vue
0 → 100644
| 1 | +<script lang="ts" setup> | ||
| 2 | + import { PageWrapper } from '/@/components/Page'; | ||
| 3 | + import { BasicForm, useForm } from '/@/components/Form'; | ||
| 4 | + import { List, Button, Tooltip, Card, PaginationProps, Image } from 'ant-design-vue'; | ||
| 5 | + import { ReloadOutlined, EyeOutlined, FormOutlined, MoreOutlined } from '@ant-design/icons-vue'; | ||
| 6 | + import { computed, onMounted, reactive, ref, unref } from 'vue'; | ||
| 7 | + import { CardLayoutButton, Mode, ModeSwitchButton } from '/@/components/Widget'; | ||
| 8 | + import { Authority } from '/@/components/Authority'; | ||
| 9 | + import { | ||
| 10 | + deviceConfigDelete, | ||
| 11 | + deviceConfigGetQuery, | ||
| 12 | + setDeviceProfileIsDefaultApi, | ||
| 13 | + } from '/@/api/device/deviceConfigApi'; | ||
| 14 | + import { ProfileRecord } from '/@/api/device/model/deviceConfigModel'; | ||
| 15 | + import { Dropdown } from '/@/components/Dropdown'; | ||
| 16 | + import { defaultObj, searchFormSchema, DeviceTypeName } from './device.profile.data'; | ||
| 17 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
| 18 | + import { useSyncConfirm } from '/@/hooks/component/useSyncConfirm'; | ||
| 19 | + import DeviceProfileModal from './DeviceProfileModal.vue'; | ||
| 20 | + import DeviceProfileDrawer from './DeviceProfileDrawer.vue'; | ||
| 21 | + import { useModal } from '/@/components/Modal'; | ||
| 22 | + import { useDrawer } from '/@/components/Drawer'; | ||
| 23 | + | ||
| 24 | + defineProps<{ | ||
| 25 | + mode: Mode; | ||
| 26 | + }>(); | ||
| 27 | + | ||
| 28 | + const emit = defineEmits(['changeMode']); | ||
| 29 | + | ||
| 30 | + enum DropMenuEvent { | ||
| 31 | + SET_DEFAULT = 'setDefault', | ||
| 32 | + DELETE = 'delete', | ||
| 33 | + } | ||
| 34 | + const IMAGE_FALLBACK = | ||
| 35 | + 'data:image/png;base64,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'; | ||
| 36 | + | ||
| 37 | + const { createMessage } = useMessage(); | ||
| 38 | + const { createSyncConfirm } = useSyncConfirm(); | ||
| 39 | + | ||
| 40 | + const [register, { getFieldsValue }] = useForm({ | ||
| 41 | + showAdvancedButton: true, | ||
| 42 | + labelWidth: 100, | ||
| 43 | + compact: true, | ||
| 44 | + baseColProps: { span: 8 }, | ||
| 45 | + schemas: searchFormSchema, | ||
| 46 | + submitFunc: async () => { | ||
| 47 | + getDataSource(); | ||
| 48 | + }, | ||
| 49 | + }); | ||
| 50 | + | ||
| 51 | + const [registerModal, { openModal }] = useModal(); | ||
| 52 | + const [registerDrawer, { openDrawer }] = useDrawer(); | ||
| 53 | + | ||
| 54 | + const loading = ref(false); | ||
| 55 | + | ||
| 56 | + const pagination = reactive<PaginationProps>({ | ||
| 57 | + size: 'small', | ||
| 58 | + showTotal: (total: number) => `共 ${total} 条数据`, | ||
| 59 | + current: 1, | ||
| 60 | + onChange: (page: number) => { | ||
| 61 | + pagination.current = page; | ||
| 62 | + getDataSource(); | ||
| 63 | + }, | ||
| 64 | + }); | ||
| 65 | + | ||
| 66 | + const dataSource = ref<ProfileRecord[]>([]); | ||
| 67 | + | ||
| 68 | + const colNumber = ref(4); | ||
| 69 | + | ||
| 70 | + const getSelectAllFlag = computed(() => { | ||
| 71 | + return unref(dataSource).every((item) => item.checked); | ||
| 72 | + }); | ||
| 73 | + | ||
| 74 | + const getCheckedRecord = computed(() => { | ||
| 75 | + return unref(dataSource) | ||
| 76 | + .filter((item) => item.checked) | ||
| 77 | + .map((item) => item.id); | ||
| 78 | + }); | ||
| 79 | + | ||
| 80 | + const getDataSource = async () => { | ||
| 81 | + try { | ||
| 82 | + loading.value = true; | ||
| 83 | + const params = getFieldsValue(); | ||
| 84 | + const { items, total } = await deviceConfigGetQuery({ | ||
| 85 | + page: pagination.current, | ||
| 86 | + pageSize: unref(colNumber) * 2, | ||
| 87 | + ...params, | ||
| 88 | + }); | ||
| 89 | + pagination.total = total; | ||
| 90 | + dataSource.value = items.map((item) => ({ ...item, checked: false })); | ||
| 91 | + } catch (error) { | ||
| 92 | + } finally { | ||
| 93 | + loading.value = false; | ||
| 94 | + } | ||
| 95 | + }; | ||
| 96 | + | ||
| 97 | + const handleModeChange = (mode: Mode) => { | ||
| 98 | + emit('changeMode', mode); | ||
| 99 | + }; | ||
| 100 | + | ||
| 101 | + const handleCheckCard = (item: ProfileRecord) => { | ||
| 102 | + item.checked = !item.checked; | ||
| 103 | + }; | ||
| 104 | + | ||
| 105 | + const handleSelectAll = () => { | ||
| 106 | + dataSource.value = unref(dataSource).map((item) => { | ||
| 107 | + return { | ||
| 108 | + ...item, | ||
| 109 | + checked: !unref(getSelectAllFlag), | ||
| 110 | + }; | ||
| 111 | + }); | ||
| 112 | + }; | ||
| 113 | + | ||
| 114 | + const handleCreate = () => { | ||
| 115 | + openModal(true, { | ||
| 116 | + isUpdate: false, | ||
| 117 | + }); | ||
| 118 | + }; | ||
| 119 | + | ||
| 120 | + const handleShowDetail = (record: ProfileRecord) => { | ||
| 121 | + openDrawer(true, { record }); | ||
| 122 | + }; | ||
| 123 | + | ||
| 124 | + const handleUpdate = (record: ProfileRecord) => { | ||
| 125 | + openModal(true, { | ||
| 126 | + record, | ||
| 127 | + isUpdate: true, | ||
| 128 | + }); | ||
| 129 | + }; | ||
| 130 | + | ||
| 131 | + const handleDelete = async (id: string[]) => { | ||
| 132 | + try { | ||
| 133 | + await createSyncConfirm({ iconType: 'warning', content: '是否确认删除操作?' }); | ||
| 134 | + await deviceConfigDelete(id); | ||
| 135 | + createMessage.success('删除成功'); | ||
| 136 | + await getDataSource(); | ||
| 137 | + } catch (error) { | ||
| 138 | + throw error; | ||
| 139 | + } | ||
| 140 | + }; | ||
| 141 | + | ||
| 142 | + const handleSetDefault = async (record: ProfileRecord) => { | ||
| 143 | + try { | ||
| 144 | + const { tbProfileId } = record; | ||
| 145 | + const data = await setDeviceProfileIsDefaultApi(tbProfileId, 'default', defaultObj); | ||
| 146 | + if (!data) return createMessage.error('设置该产品为默认失败'); | ||
| 147 | + createMessage.success('设置该产品为默认成功'); | ||
| 148 | + await getDataSource(); | ||
| 149 | + } catch (error) { | ||
| 150 | + throw error; | ||
| 151 | + } | ||
| 152 | + }; | ||
| 153 | + | ||
| 154 | + onMounted(() => { | ||
| 155 | + getDataSource(); | ||
| 156 | + }); | ||
| 157 | +</script> | ||
| 158 | + | ||
| 159 | +<template> | ||
| 160 | + <PageWrapper dense contentFullHeight contentClass="flex"> | ||
| 161 | + <section class="flex-auto p-4 w-full profile-list"> | ||
| 162 | + <div class="flex-auto w-full bg-light-50 dark:bg-dark-900 p-4"> | ||
| 163 | + <BasicForm @register="register" /> | ||
| 164 | + </div> | ||
| 165 | + <List | ||
| 166 | + ref="listEl" | ||
| 167 | + :loading="loading" | ||
| 168 | + class="flex-auto bg-light-50 dark:bg-dark-900 !p-2 !mt-4" | ||
| 169 | + position="bottom" | ||
| 170 | + :pagination="pagination" | ||
| 171 | + :data-source="dataSource" | ||
| 172 | + :grid="{ gutter: 4, column: colNumber }" | ||
| 173 | + > | ||
| 174 | + <template #header> | ||
| 175 | + <div class="flex gap-3 justify-end"> | ||
| 176 | + <Button type="primary" @click="handleCreate">新增产品</Button> | ||
| 177 | + <Button type="primary" @click="handleSelectAll"> | ||
| 178 | + {{ getSelectAllFlag ? '反选' : '全选' }} | ||
| 179 | + </Button> | ||
| 180 | + <Button | ||
| 181 | + type="primary" | ||
| 182 | + danger | ||
| 183 | + :disabled="!getCheckedRecord.length" | ||
| 184 | + @click="handleDelete(getCheckedRecord)" | ||
| 185 | + > | ||
| 186 | + 批量删除 | ||
| 187 | + </Button> | ||
| 188 | + <ModeSwitchButton :value="$props.mode" @change="handleModeChange" /> | ||
| 189 | + <CardLayoutButton v-model:value="colNumber" @change="getDataSource" /> | ||
| 190 | + <Tooltip title="刷新"> | ||
| 191 | + <Button type="primary" @click="getDataSource"> | ||
| 192 | + <ReloadOutlined :spin="loading" /> | ||
| 193 | + </Button> | ||
| 194 | + </Tooltip> | ||
| 195 | + </div> | ||
| 196 | + </template> | ||
| 197 | + <template #renderItem="{ item }"> | ||
| 198 | + <List.Item> | ||
| 199 | + <Card | ||
| 200 | + hoverable | ||
| 201 | + @click="handleCheckCard(item)" | ||
| 202 | + :class="item.checked ? '!border-blue-500 !border-2' : ''" | ||
| 203 | + > | ||
| 204 | + <template #cover> | ||
| 205 | + <div class="h-full w-full !flex justify-center items-center text-center"> | ||
| 206 | + <Image | ||
| 207 | + @click.stop | ||
| 208 | + :height="144" | ||
| 209 | + :src="item.image" | ||
| 210 | + placeholder | ||
| 211 | + :fallback="IMAGE_FALLBACK" | ||
| 212 | + /> | ||
| 213 | + </div> | ||
| 214 | + </template> | ||
| 215 | + <template class="ant-card-actions" #actions> | ||
| 216 | + <Authority> | ||
| 217 | + <Tooltip title="详情"> | ||
| 218 | + <EyeOutlined key="setting" @click.stop="handleShowDetail(item)" /> | ||
| 219 | + </Tooltip> | ||
| 220 | + </Authority> | ||
| 221 | + <Authority> | ||
| 222 | + <Tooltip title="编辑"> | ||
| 223 | + <FormOutlined key="edit" @click.stop="handleUpdate(item)" /> | ||
| 224 | + </Tooltip> | ||
| 225 | + </Authority> | ||
| 226 | + <Dropdown | ||
| 227 | + :trigger="['hover']" | ||
| 228 | + :drop-menu-list="[ | ||
| 229 | + { | ||
| 230 | + text: '默认', | ||
| 231 | + event: DropMenuEvent.SET_DEFAULT, | ||
| 232 | + icon: 'ant-design:unordered-list-outlined', | ||
| 233 | + onClick: handleSetDefault.bind(null, item), | ||
| 234 | + }, | ||
| 235 | + { | ||
| 236 | + text: '删除', | ||
| 237 | + event: DropMenuEvent.DELETE, | ||
| 238 | + icon: 'ant-design:delete-outlined', | ||
| 239 | + onClick: handleDelete.bind(null, [item.id]), | ||
| 240 | + }, | ||
| 241 | + ]" | ||
| 242 | + > | ||
| 243 | + <MoreOutlined @click.stop class="transform rotate-90" /> | ||
| 244 | + </Dropdown> | ||
| 245 | + </template> | ||
| 246 | + <Card.Meta> | ||
| 247 | + <template #title> | ||
| 248 | + <span class="truncate"> {{ item.name }} </span> | ||
| 249 | + </template> | ||
| 250 | + <template #description> | ||
| 251 | + <div class="truncate h-11"> | ||
| 252 | + <div class="truncate">{{ DeviceTypeName[item.deviceType] }} </div> | ||
| 253 | + <div class="truncate">{{ item.transportType }} </div> | ||
| 254 | + </div> | ||
| 255 | + </template> | ||
| 256 | + </Card.Meta> | ||
| 257 | + </Card> | ||
| 258 | + </List.Item> | ||
| 259 | + </template> | ||
| 260 | + </List> | ||
| 261 | + </section> | ||
| 262 | + <DeviceProfileModal @register="registerModal" @success="getDataSource" /> | ||
| 263 | + <DeviceProfileDrawer @register="registerDrawer" /> | ||
| 264 | + </PageWrapper> | ||
| 265 | +</template> | ||
| 266 | + | ||
| 267 | +<style lang="less" scoped> | ||
| 268 | + .profile-list:deep(.ant-image-img) { | ||
| 269 | + width: 100% !important; | ||
| 270 | + height: 100% !important; | ||
| 271 | + } | ||
| 272 | +</style> |
src/views/device/profiles/TableMode.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div> | ||
| 3 | + <BasicTable | ||
| 4 | + class="devide-profiles" | ||
| 5 | + @register="registerTable" | ||
| 6 | + :rowSelection="{ type: 'checkbox' }" | ||
| 7 | + :clickToRowSelect="false" | ||
| 8 | + > | ||
| 9 | + <template #toolbar> | ||
| 10 | + <Authority value="api:yt:deviceProfile:post"> | ||
| 11 | + <a-button type="primary" @click="handleCreate"> 新增产品 </a-button> | ||
| 12 | + </Authority> | ||
| 13 | + <Authority value="api:yt:deviceProfile:import"> | ||
| 14 | + <ImpExcel @success="loadDataSuccess" dateFormat="YYYY-MM-DD"> | ||
| 15 | + <a-button @click="handleImport"> 导入产品 </a-button> | ||
| 16 | + </ImpExcel> | ||
| 17 | + </Authority> | ||
| 18 | + <Authority value="api:yt:deviceProfile:delete"> | ||
| 19 | + <Popconfirm | ||
| 20 | + title="您确定要批量删除数据" | ||
| 21 | + ok-text="确定" | ||
| 22 | + cancel-text="取消" | ||
| 23 | + @confirm="handleDeleteOrBatchDelete(null)" | ||
| 24 | + > | ||
| 25 | + <a-button type="primary" color="error" :disabled="hasBatchDelete"> 批量删除 </a-button> | ||
| 26 | + </Popconfirm> | ||
| 27 | + </Authority> | ||
| 28 | + <ModeSwitchButton :value="$props.mode" @change="handleModeChange" /> | ||
| 29 | + </template> | ||
| 30 | + <template #img="{ record }"> | ||
| 31 | + <TableImg | ||
| 32 | + :size="30" | ||
| 33 | + :showBadge="false" | ||
| 34 | + :simpleShow="true" | ||
| 35 | + :imgList=" | ||
| 36 | + typeof record.image !== 'undefined' && record.image !== '' && record.image != null | ||
| 37 | + ? [record.image] | ||
| 38 | + : null | ||
| 39 | + " | ||
| 40 | + /> | ||
| 41 | + </template> | ||
| 42 | + <template #action="{ record }"> | ||
| 43 | + <TableAction | ||
| 44 | + :actions="[ | ||
| 45 | + { | ||
| 46 | + label: '详情', | ||
| 47 | + auth: 'api:yt:deviceProfile:get', | ||
| 48 | + icon: 'ant-design:eye-outlined', | ||
| 49 | + onClick: handleDetailView.bind(null, record), | ||
| 50 | + }, | ||
| 51 | + { | ||
| 52 | + label: '编辑', | ||
| 53 | + auth: 'api:yt:deviceProfile:update', | ||
| 54 | + icon: 'clarity:note-edit-line', | ||
| 55 | + onClick: handleEdit.bind(null, record), | ||
| 56 | + ifShow: () => { | ||
| 57 | + return record.name !== 'default' ? true : false; | ||
| 58 | + }, | ||
| 59 | + }, | ||
| 60 | + ]" | ||
| 61 | + :drop-down-actions="[ | ||
| 62 | + { | ||
| 63 | + label: '默认', | ||
| 64 | + icon: 'ant-design:profile-outlined', | ||
| 65 | + onClick: handleSetDefault.bind(null, record), | ||
| 66 | + ifShow: () => { | ||
| 67 | + return record.default === false; | ||
| 68 | + }, | ||
| 69 | + }, | ||
| 70 | + { | ||
| 71 | + label: '导出', | ||
| 72 | + auth: 'api:yt:deviceProfile:export', | ||
| 73 | + icon: 'ant-design:login-outlined', | ||
| 74 | + onClick: handleExport.bind(null, record), | ||
| 75 | + }, | ||
| 76 | + { | ||
| 77 | + label: '删除', | ||
| 78 | + auth: 'api:yt:deviceProfile:delete', | ||
| 79 | + icon: 'ant-design:delete-outlined', | ||
| 80 | + color: 'error', | ||
| 81 | + popConfirm: { | ||
| 82 | + title: '是否确认删除', | ||
| 83 | + confirm: handleDeleteOrBatchDelete.bind(null, record), | ||
| 84 | + }, | ||
| 85 | + ifShow: () => { | ||
| 86 | + return record.default === false && record.name !== 'default'; | ||
| 87 | + }, | ||
| 88 | + }, | ||
| 89 | + ]" | ||
| 90 | + /> | ||
| 91 | + </template> | ||
| 92 | + </BasicTable> | ||
| 93 | + <DeviceProfileModal @register="registerModal" @success="handleSuccess" /> | ||
| 94 | + <DeviceProfileDrawer @register="registerDrawer" /> | ||
| 95 | + <ExpExcelModal | ||
| 96 | + ref="expExcelModalRef" | ||
| 97 | + @register="registerExportModal" | ||
| 98 | + @success="defaultHeader" | ||
| 99 | + /> | ||
| 100 | + </div> | ||
| 101 | +</template> | ||
| 102 | +<script lang="ts" setup> | ||
| 103 | + import { ref, nextTick, onUnmounted } from 'vue'; | ||
| 104 | + import { BasicTable, TableImg, useTable, TableAction, BasicColumn } from '/@/components/Table'; | ||
| 105 | + import { columns, searchFormSchema, defaultObj } from './device.profile.data'; | ||
| 106 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
| 107 | + import { | ||
| 108 | + deviceConfigGetQuery, | ||
| 109 | + deviceConfigDelete, | ||
| 110 | + setDeviceProfileIsDefaultApi, | ||
| 111 | + } from '/@/api/device/deviceConfigApi'; | ||
| 112 | + import { useModal } from '/@/components/Modal'; | ||
| 113 | + import { useDrawer } from '/@/components/Drawer'; | ||
| 114 | + import DeviceProfileModal from '/@/views/device/profiles/DeviceProfileModal.vue'; | ||
| 115 | + import { ImpExcel, ExcelData } from '/@/components/Excel'; | ||
| 116 | + import { jsonToSheetXlsx, ExpExcelModal, ExportModalResult } from '/@/components/Excel'; | ||
| 117 | + import { Authority } from '/@/components/Authority'; | ||
| 118 | + import { useBatchDelete } from '/@/hooks/web/useBatchDelete'; | ||
| 119 | + import { Popconfirm } from 'ant-design-vue'; | ||
| 120 | + import DeviceProfileDrawer from './DeviceProfileDrawer.vue'; | ||
| 121 | + import { Mode, ModeSwitchButton } from '/@/components/Widget'; | ||
| 122 | + | ||
| 123 | + defineProps<{ | ||
| 124 | + mode: Mode; | ||
| 125 | + }>(); | ||
| 126 | + | ||
| 127 | + const emit = defineEmits(['changeMode']); | ||
| 128 | + | ||
| 129 | + const exportData: any = ref([]); | ||
| 130 | + const expExcelModalRef: any = ref(null); | ||
| 131 | + const getPathUrl = ref(''); | ||
| 132 | + const getPathUrlName = ref(''); | ||
| 133 | + const disabled = ref(true); | ||
| 134 | + const onCloseVal = ref(0); | ||
| 135 | + const immediateStatus = ref(false); | ||
| 136 | + const { createMessage } = useMessage(); | ||
| 137 | + const [registerModal, { openModal }] = useModal(); | ||
| 138 | + const [registerExportModal, { openModal: openModalExcel }] = useModal(); | ||
| 139 | + const [registerTable, { setProps, reload, setTableData, getForm }] = useTable({ | ||
| 140 | + title: '产品列表', | ||
| 141 | + clickToRowSelect: false, | ||
| 142 | + api: deviceConfigGetQuery, | ||
| 143 | + immediate: immediateStatus.value, | ||
| 144 | + columns, | ||
| 145 | + formConfig: { | ||
| 146 | + labelWidth: 120, | ||
| 147 | + schemas: searchFormSchema, | ||
| 148 | + }, | ||
| 149 | + rowKey: 'id', | ||
| 150 | + useSearchForm: true, | ||
| 151 | + showTableSetting: true, | ||
| 152 | + bordered: true, | ||
| 153 | + showIndexColumn: false, | ||
| 154 | + actionColumn: { | ||
| 155 | + width: 200, | ||
| 156 | + title: '操作', | ||
| 157 | + dataIndex: 'action', | ||
| 158 | + slots: { customRender: 'action' }, | ||
| 159 | + fixed: 'right', | ||
| 160 | + }, | ||
| 161 | + }); | ||
| 162 | + const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions } = useBatchDelete( | ||
| 163 | + deviceConfigDelete, | ||
| 164 | + handleSuccess, | ||
| 165 | + setProps | ||
| 166 | + ); | ||
| 167 | + selectionOptions.rowSelection.getCheckboxProps = (record: Recordable) => { | ||
| 168 | + // Demo:status为1的选择框禁用 | ||
| 169 | + if (record.default === true) { | ||
| 170 | + return { disabled: true }; | ||
| 171 | + } else if (record.name == 'default') { | ||
| 172 | + return { disabled: true }; | ||
| 173 | + } else { | ||
| 174 | + return { disabled: false }; | ||
| 175 | + } | ||
| 176 | + }; | ||
| 177 | + nextTick(() => { | ||
| 178 | + setProps(selectionOptions); | ||
| 179 | + }); | ||
| 180 | + /** | ||
| 181 | + *@param url,name | ||
| 182 | + **/ | ||
| 183 | + function getParam(url, name) { | ||
| 184 | + try { | ||
| 185 | + let reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)'); | ||
| 186 | + let r = url.split('?')[1].match(reg); | ||
| 187 | + if (r != null) { | ||
| 188 | + return r[2]; | ||
| 189 | + } | ||
| 190 | + return ''; //如果此处只写return;则返回的是undefined | ||
| 191 | + } catch (e) { | ||
| 192 | + return ''; //如果此处只写return;则返回的是undefined | ||
| 193 | + } | ||
| 194 | + } | ||
| 195 | + getPathUrl.value = window.location.href; | ||
| 196 | + const name = 'name'; | ||
| 197 | + const getName = getParam(getPathUrl.value, name); | ||
| 198 | + getPathUrlName.value = decodeURIComponent(getName); | ||
| 199 | + | ||
| 200 | + const setRowClassName = async () => { | ||
| 201 | + if (getPathUrlName.value !== '') { | ||
| 202 | + const { items } = await deviceConfigGetQuery({ | ||
| 203 | + page: 1, | ||
| 204 | + pageSize: 10, | ||
| 205 | + name: getPathUrlName.value, | ||
| 206 | + }); | ||
| 207 | + nextTick(() => { | ||
| 208 | + setTableData(items); | ||
| 209 | + const { setFieldsValue, resetFields } = getForm(); | ||
| 210 | + setFieldsValue({ | ||
| 211 | + name: getPathUrlName.value, | ||
| 212 | + }); | ||
| 213 | + if (onCloseVal.value == 1) { | ||
| 214 | + resetFields(); | ||
| 215 | + } | ||
| 216 | + }); | ||
| 217 | + } else { | ||
| 218 | + setTimeout(() => { | ||
| 219 | + reload(); | ||
| 220 | + }, 80); | ||
| 221 | + } | ||
| 222 | + }; | ||
| 223 | + setRowClassName(); | ||
| 224 | + onUnmounted(() => { | ||
| 225 | + getPathUrlName.value = ''; | ||
| 226 | + onCloseVal.value = 1; | ||
| 227 | + }); | ||
| 228 | + const tableListRef = ref< | ||
| 229 | + { | ||
| 230 | + title: string; | ||
| 231 | + columns?: any[]; | ||
| 232 | + dataSource?: any[]; | ||
| 233 | + }[] | ||
| 234 | + >([]); | ||
| 235 | + | ||
| 236 | + function loadDataSuccess(excelDataList: ExcelData[]) { | ||
| 237 | + tableListRef.value = []; | ||
| 238 | + console.log(excelDataList); | ||
| 239 | + for (const excelData of excelDataList) { | ||
| 240 | + const { | ||
| 241 | + header, | ||
| 242 | + results, | ||
| 243 | + meta: { sheetName }, | ||
| 244 | + } = excelData; | ||
| 245 | + const columns: BasicColumn[] = []; | ||
| 246 | + for (const title of header) { | ||
| 247 | + columns.push({ title, dataIndex: title }); | ||
| 248 | + } | ||
| 249 | + tableListRef.value.push({ title: sheetName, dataSource: results, columns }); | ||
| 250 | + } | ||
| 251 | + } | ||
| 252 | + //新增 | ||
| 253 | + function handleCreate() { | ||
| 254 | + openModal(true, { | ||
| 255 | + isUpdate: false, | ||
| 256 | + }); | ||
| 257 | + } | ||
| 258 | + //编辑 | ||
| 259 | + function handleEdit(record: Recordable) { | ||
| 260 | + openModal(true, { | ||
| 261 | + record, | ||
| 262 | + isUpdate: true, | ||
| 263 | + }); | ||
| 264 | + } | ||
| 265 | + | ||
| 266 | + const [registerDrawer, { openDrawer }] = useDrawer(); | ||
| 267 | + //详情 | ||
| 268 | + function handleDetailView(record: Recordable) { | ||
| 269 | + openDrawer(true, { record }); | ||
| 270 | + } | ||
| 271 | + | ||
| 272 | + function defaultHeader({ filename, bookType }: ExportModalResult) { | ||
| 273 | + // 默认Object.keys(data[0])作为header | ||
| 274 | + const data = exportData.value; | ||
| 275 | + jsonToSheetXlsx({ | ||
| 276 | + data, | ||
| 277 | + filename, | ||
| 278 | + write2excelOpts: { | ||
| 279 | + bookType, | ||
| 280 | + }, | ||
| 281 | + }); | ||
| 282 | + } | ||
| 283 | + //导出 | ||
| 284 | + const handleExport = (record: Recordable) => { | ||
| 285 | + exportData.value = []; | ||
| 286 | + exportData.value.push({ | ||
| 287 | + createTime: record.createTime, | ||
| 288 | + description: record.description, | ||
| 289 | + name: record.name, | ||
| 290 | + }); | ||
| 291 | + nextTick(() => { | ||
| 292 | + openModalExcel(); | ||
| 293 | + expExcelModalRef.value?.clearFieldFunc(); | ||
| 294 | + }); | ||
| 295 | + }; | ||
| 296 | + //导入 | ||
| 297 | + function handleImport() { | ||
| 298 | + console.log('record'); | ||
| 299 | + } | ||
| 300 | + function handleSuccess() { | ||
| 301 | + reload(); | ||
| 302 | + } | ||
| 303 | + | ||
| 304 | + const handleSetDefault = async (record: Recordable) => { | ||
| 305 | + let id = record.tbProfileId; | ||
| 306 | + const data = await setDeviceProfileIsDefaultApi(id, 'default', defaultObj); | ||
| 307 | + if (!data) return createMessage.error('设置该产品为默认失败'); | ||
| 308 | + createMessage.success('设置该产品为默认成功'); | ||
| 309 | + reload(); | ||
| 310 | + disabled.value = true; | ||
| 311 | + }; | ||
| 312 | + | ||
| 313 | + const handleModeChange = (value: Mode) => { | ||
| 314 | + emit('changeMode', value); | ||
| 315 | + }; | ||
| 316 | +</script> | ||
| 317 | + | ||
| 318 | +<style lang="css"> | ||
| 319 | + .devide-profiles .rowcolor { | ||
| 320 | + color: red; | ||
| 321 | + } | ||
| 322 | + | ||
| 323 | + .devide-profiles .rowcolor2 { | ||
| 324 | + background: #a2c3e6; | ||
| 325 | + } | ||
| 326 | +</style> |
| @@ -9,6 +9,17 @@ import { FormField, FunctionType } from './step/cpns/physical/cpns/config'; | @@ -9,6 +9,17 @@ import { FormField, FunctionType } from './step/cpns/physical/cpns/config'; | ||
| 9 | import { h } from 'vue'; | 9 | import { h } from 'vue'; |
| 10 | import { Tag } from 'ant-design-vue'; | 10 | import { Tag } from 'ant-design-vue'; |
| 11 | 11 | ||
| 12 | +export enum Mode { | ||
| 13 | + CARD = 'card', | ||
| 14 | + TABLE = 'table', | ||
| 15 | +} | ||
| 16 | + | ||
| 17 | +export enum DeviceTypeName { | ||
| 18 | + DIRECT_CONNECTION = '直连设备', | ||
| 19 | + GATEWAY = '网关设备', | ||
| 20 | + SENSOR = '网关子设备', | ||
| 21 | +} | ||
| 22 | + | ||
| 12 | export enum ModelOfMatterPermission { | 23 | export enum ModelOfMatterPermission { |
| 13 | CREATE = 'api:yt:things_model:post', | 24 | CREATE = 'api:yt:things_model:post', |
| 14 | UPDATE = 'api:yt:things_model:put', | 25 | UPDATE = 'api:yt:things_model:put', |
| 1 | -<template> | ||
| 2 | - <div> | ||
| 3 | - <BasicTable | ||
| 4 | - class="devide-profiles" | ||
| 5 | - @register="registerTable" | ||
| 6 | - :rowSelection="{ type: 'checkbox' }" | ||
| 7 | - :clickToRowSelect="false" | ||
| 8 | - > | ||
| 9 | - <template #toolbar> | ||
| 10 | - <Authority value="api:yt:deviceProfile:post"> | ||
| 11 | - <a-button type="primary" @click="handleCreate"> 新增产品 </a-button> | ||
| 12 | - </Authority> | ||
| 13 | - <Authority value="api:yt:deviceProfile:import"> | ||
| 14 | - <ImpExcel @success="loadDataSuccess" dateFormat="YYYY-MM-DD"> | ||
| 15 | - <a-button @click="handleImport"> 导入产品 </a-button> | ||
| 16 | - </ImpExcel> | ||
| 17 | - </Authority> | ||
| 18 | - <Authority value="api:yt:deviceProfile:delete"> | ||
| 19 | - <Popconfirm | ||
| 20 | - title="您确定要批量删除数据" | ||
| 21 | - ok-text="确定" | ||
| 22 | - cancel-text="取消" | ||
| 23 | - @confirm="handleDeleteOrBatchDelete(null)" | ||
| 24 | - > | ||
| 25 | - <a-button type="primary" color="error" :disabled="hasBatchDelete"> 批量删除 </a-button> | ||
| 26 | - </Popconfirm> | ||
| 27 | - </Authority> | ||
| 28 | - </template> | ||
| 29 | - <template #img="{ record }"> | ||
| 30 | - <TableImg | ||
| 31 | - :size="30" | ||
| 32 | - :showBadge="false" | ||
| 33 | - :simpleShow="true" | ||
| 34 | - :imgList=" | ||
| 35 | - typeof record.image !== 'undefined' && record.image !== '' && record.image != null | ||
| 36 | - ? [record.image] | ||
| 37 | - : null | ||
| 38 | - " | ||
| 39 | - /> | ||
| 40 | - </template> | ||
| 41 | - <template #action="{ record }"> | ||
| 42 | - <TableAction | ||
| 43 | - :actions="[ | ||
| 44 | - { | ||
| 45 | - label: '详情', | ||
| 46 | - auth: 'api:yt:deviceProfile:get', | ||
| 47 | - icon: 'ant-design:eye-outlined', | ||
| 48 | - onClick: handleDetailView.bind(null, record), | ||
| 49 | - }, | ||
| 50 | - { | ||
| 51 | - label: '编辑', | ||
| 52 | - auth: 'api:yt:deviceProfile:update', | ||
| 53 | - icon: 'clarity:note-edit-line', | ||
| 54 | - onClick: handleEdit.bind(null, record), | ||
| 55 | - ifShow: () => { | ||
| 56 | - return record.name !== 'default' ? true : false; | ||
| 57 | - }, | ||
| 58 | - }, | ||
| 59 | - ]" | ||
| 60 | - :drop-down-actions="[ | ||
| 61 | - { | ||
| 62 | - label: '默认', | ||
| 63 | - icon: 'ant-design:profile-outlined', | ||
| 64 | - onClick: handleSetDefault.bind(null, record), | ||
| 65 | - ifShow: () => { | ||
| 66 | - return record.default === false; | ||
| 67 | - }, | ||
| 68 | - }, | ||
| 69 | - { | ||
| 70 | - label: '导出', | ||
| 71 | - auth: 'api:yt:deviceProfile:export', | ||
| 72 | - icon: 'ant-design:login-outlined', | ||
| 73 | - onClick: handleExport.bind(null, record), | ||
| 74 | - }, | ||
| 75 | - { | ||
| 76 | - label: '删除', | ||
| 77 | - auth: 'api:yt:deviceProfile:delete', | ||
| 78 | - icon: 'ant-design:delete-outlined', | ||
| 79 | - color: 'error', | ||
| 80 | - popConfirm: { | ||
| 81 | - title: '是否确认删除', | ||
| 82 | - confirm: handleDeleteOrBatchDelete.bind(null, record), | ||
| 83 | - }, | ||
| 84 | - ifShow: () => { | ||
| 85 | - return record.default === false && record.name !== 'default'; | ||
| 86 | - }, | ||
| 87 | - }, | ||
| 88 | - ]" | ||
| 89 | - /> | ||
| 90 | - </template> | ||
| 91 | - </BasicTable> | ||
| 92 | - <DeviceProfileModal @register="registerModal" @success="handleSuccess" /> | ||
| 93 | - <DeviceProfileDrawer @register="registerDrawer" /> | ||
| 94 | - <ExpExcelModal | ||
| 95 | - ref="expExcelModalRef" | ||
| 96 | - @register="registerExportModal" | ||
| 97 | - @success="defaultHeader" | ||
| 98 | - /> | ||
| 99 | - </div> | ||
| 100 | -</template> | ||
| 101 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
| 102 | - import { ref, nextTick, onUnmounted } from 'vue'; | ||
| 103 | - import { BasicTable, TableImg, useTable, TableAction, BasicColumn } from '/@/components/Table'; | ||
| 104 | - import { columns, searchFormSchema, defaultObj } from './device.profile.data'; | ||
| 105 | - import { useMessage } from '/@/hooks/web/useMessage'; | ||
| 106 | - import { | ||
| 107 | - deviceConfigGetQuery, | ||
| 108 | - deviceConfigDelete, | ||
| 109 | - setDeviceProfileIsDefaultApi, | ||
| 110 | - } from '/@/api/device/deviceConfigApi'; | ||
| 111 | - import { useModal } from '/@/components/Modal'; | ||
| 112 | - import { useDrawer } from '/@/components/Drawer'; | ||
| 113 | - import DeviceProfileModal from '/@/views/device/profiles/DeviceProfileModal.vue'; | ||
| 114 | - import { ImpExcel, ExcelData } from '/@/components/Excel'; | ||
| 115 | - import { jsonToSheetXlsx, ExpExcelModal, ExportModalResult } from '/@/components/Excel'; | ||
| 116 | - import { Authority } from '/@/components/Authority'; | ||
| 117 | - import { useBatchDelete } from '/@/hooks/web/useBatchDelete'; | ||
| 118 | - import { Popconfirm } from 'ant-design-vue'; | ||
| 119 | - import DeviceProfileDrawer from './DeviceProfileDrawer.vue'; | ||
| 120 | - | ||
| 121 | - const exportData: any = ref([]); | ||
| 122 | - const expExcelModalRef: any = ref(null); | ||
| 123 | - const getPathUrl = ref(''); | ||
| 124 | - const getPathUrlName = ref(''); | ||
| 125 | - const disabled = ref(true); | ||
| 126 | - const onCloseVal = ref(0); | ||
| 127 | - const immediateStatus = ref(false); | ||
| 128 | - const { createMessage } = useMessage(); | ||
| 129 | - const [registerModal, { openModal }] = useModal(); | ||
| 130 | - const [registerExportModal, { openModal: openModalExcel }] = useModal(); | ||
| 131 | - const [registerTable, { setProps, reload, setTableData, getForm }] = useTable({ | ||
| 132 | - title: '产品列表', | ||
| 133 | - clickToRowSelect: false, | ||
| 134 | - api: deviceConfigGetQuery, | ||
| 135 | - immediate: immediateStatus.value, | ||
| 136 | - columns, | ||
| 137 | - formConfig: { | ||
| 138 | - labelWidth: 120, | ||
| 139 | - schemas: searchFormSchema, | ||
| 140 | - }, | ||
| 141 | - rowKey: 'id', | ||
| 142 | - useSearchForm: true, | ||
| 143 | - showTableSetting: true, | ||
| 144 | - bordered: true, | ||
| 145 | - showIndexColumn: false, | ||
| 146 | - actionColumn: { | ||
| 147 | - width: 200, | ||
| 148 | - title: '操作', | ||
| 149 | - dataIndex: 'action', | ||
| 150 | - slots: { customRender: 'action' }, | ||
| 151 | - fixed: 'right', | ||
| 152 | - }, | ||
| 153 | - }); | ||
| 154 | - const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions } = useBatchDelete( | ||
| 155 | - deviceConfigDelete, | ||
| 156 | - handleSuccess, | ||
| 157 | - setProps | ||
| 158 | - ); | ||
| 159 | - selectionOptions.rowSelection.getCheckboxProps = (record: Recordable) => { | ||
| 160 | - // Demo:status为1的选择框禁用 | ||
| 161 | - if (record.default === true) { | ||
| 162 | - return { disabled: true }; | ||
| 163 | - } else if (record.name == 'default') { | ||
| 164 | - return { disabled: true }; | ||
| 165 | - } else { | ||
| 166 | - return { disabled: false }; | ||
| 167 | - } | ||
| 168 | - }; | ||
| 169 | - nextTick(() => { | ||
| 170 | - setProps(selectionOptions); | ||
| 171 | - }); | ||
| 172 | - /** | ||
| 173 | - *@param url,name | ||
| 174 | - **/ | ||
| 175 | - function getParam(url, name) { | ||
| 176 | - try { | ||
| 177 | - let reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)'); | ||
| 178 | - let r = url.split('?')[1].match(reg); | ||
| 179 | - if (r != null) { | ||
| 180 | - return r[2]; | ||
| 181 | - } | ||
| 182 | - return ''; //如果此处只写return;则返回的是undefined | ||
| 183 | - } catch (e) { | ||
| 184 | - return ''; //如果此处只写return;则返回的是undefined | ||
| 185 | - } | ||
| 186 | - } | ||
| 187 | - getPathUrl.value = window.location.href; | ||
| 188 | - const name = 'name'; | ||
| 189 | - const getName = getParam(getPathUrl.value, name); | ||
| 190 | - getPathUrlName.value = decodeURIComponent(getName); | ||
| 191 | - | ||
| 192 | - const setRowClassName = async () => { | ||
| 193 | - if (getPathUrlName.value !== '') { | ||
| 194 | - const { items } = await deviceConfigGetQuery({ | ||
| 195 | - page: 1, | ||
| 196 | - pageSize: 10, | ||
| 197 | - name: getPathUrlName.value, | ||
| 198 | - }); | ||
| 199 | - nextTick(() => { | ||
| 200 | - setTableData(items); | ||
| 201 | - const { setFieldsValue, resetFields } = getForm(); | ||
| 202 | - setFieldsValue({ | ||
| 203 | - name: getPathUrlName.value, | ||
| 204 | - }); | ||
| 205 | - if (onCloseVal.value == 1) { | ||
| 206 | - resetFields(); | ||
| 207 | - } | ||
| 208 | - }); | ||
| 209 | - } else { | ||
| 210 | - setTimeout(() => { | ||
| 211 | - reload(); | ||
| 212 | - }, 80); | ||
| 213 | - } | ||
| 214 | - }; | ||
| 215 | - setRowClassName(); | ||
| 216 | - onUnmounted(() => { | ||
| 217 | - getPathUrlName.value = ''; | ||
| 218 | - onCloseVal.value = 1; | ||
| 219 | - }); | ||
| 220 | - const tableListRef = ref< | ||
| 221 | - { | ||
| 222 | - title: string; | ||
| 223 | - columns?: any[]; | ||
| 224 | - dataSource?: any[]; | ||
| 225 | - }[] | ||
| 226 | - >([]); | ||
| 227 | - | ||
| 228 | - function loadDataSuccess(excelDataList: ExcelData[]) { | ||
| 229 | - tableListRef.value = []; | ||
| 230 | - console.log(excelDataList); | ||
| 231 | - for (const excelData of excelDataList) { | ||
| 232 | - const { | ||
| 233 | - header, | ||
| 234 | - results, | ||
| 235 | - meta: { sheetName }, | ||
| 236 | - } = excelData; | ||
| 237 | - const columns: BasicColumn[] = []; | ||
| 238 | - for (const title of header) { | ||
| 239 | - columns.push({ title, dataIndex: title }); | ||
| 240 | - } | ||
| 241 | - tableListRef.value.push({ title: sheetName, dataSource: results, columns }); | ||
| 242 | - } | ||
| 243 | - } | ||
| 244 | - //新增 | ||
| 245 | - function handleCreate() { | ||
| 246 | - openModal(true, { | ||
| 247 | - isUpdate: false, | ||
| 248 | - }); | ||
| 249 | - } | ||
| 250 | - //编辑 | ||
| 251 | - function handleEdit(record: Recordable) { | ||
| 252 | - openModal(true, { | ||
| 253 | - record, | ||
| 254 | - isUpdate: true, | ||
| 255 | - }); | ||
| 256 | - } | ||
| 257 | - | ||
| 258 | - const [registerDrawer, { openDrawer }] = useDrawer(); | ||
| 259 | - //详情 | ||
| 260 | - function handleDetailView(record: Recordable) { | ||
| 261 | - openDrawer(true, { record }); | ||
| 262 | - } | ||
| 263 | - | ||
| 264 | - function defaultHeader({ filename, bookType }: ExportModalResult) { | ||
| 265 | - // 默认Object.keys(data[0])作为header | ||
| 266 | - const data = exportData.value; | ||
| 267 | - jsonToSheetXlsx({ | ||
| 268 | - data, | ||
| 269 | - filename, | ||
| 270 | - write2excelOpts: { | ||
| 271 | - bookType, | ||
| 272 | - }, | ||
| 273 | - }); | ||
| 274 | - } | ||
| 275 | - //导出 | ||
| 276 | - const handleExport = (record: Recordable) => { | ||
| 277 | - exportData.value = []; | ||
| 278 | - exportData.value.push({ | ||
| 279 | - createTime: record.createTime, | ||
| 280 | - description: record.description, | ||
| 281 | - name: record.name, | ||
| 282 | - }); | ||
| 283 | - nextTick(() => { | ||
| 284 | - openModalExcel(); | ||
| 285 | - expExcelModalRef.value?.clearFieldFunc(); | ||
| 286 | - }); | ||
| 287 | - }; | ||
| 288 | - //导入 | ||
| 289 | - function handleImport() { | ||
| 290 | - console.log('record'); | ||
| 291 | - } | ||
| 292 | - function handleSuccess() { | ||
| 293 | - reload(); | ||
| 294 | - } | ||
| 295 | - | ||
| 296 | - const handleSetDefault = async (record: Recordable) => { | ||
| 297 | - let id = record.tbProfileId; | ||
| 298 | - const data = await setDeviceProfileIsDefaultApi(id, 'default', defaultObj); | ||
| 299 | - if (!data) return createMessage.error('设置该产品为默认失败'); | ||
| 300 | - createMessage.success('设置该产品为默认成功'); | ||
| 301 | - reload(); | ||
| 302 | - disabled.value = true; | 2 | + import { ref } from 'vue'; |
| 3 | + import TableMode from './TableMode.vue'; | ||
| 4 | + import CardMode from './CardMode.vue'; | ||
| 5 | + import { Mode } from '/@/components/Widget'; | ||
| 6 | + const mode = ref(Mode.CARD); | ||
| 7 | + | ||
| 8 | + const handleChangeMode = (flag: Mode) => { | ||
| 9 | + mode.value = flag; | ||
| 303 | }; | 10 | }; |
| 304 | </script> | 11 | </script> |
| 305 | 12 | ||
| 306 | -<style lang="css"> | ||
| 307 | - .devide-profiles .rowcolor { | ||
| 308 | - color: red; | ||
| 309 | - } | ||
| 310 | - | ||
| 311 | - .devide-profiles .rowcolor2 { | ||
| 312 | - background: #a2c3e6; | ||
| 313 | - } | ||
| 314 | -</style> | 13 | +<template> |
| 14 | + <section> | ||
| 15 | + <CardMode v-if="mode === Mode.CARD" :mode="mode" @change-mode="handleChangeMode" /> | ||
| 16 | + <TableMode v-if="mode === Mode.TABLE" :mode="mode" @change-mode="handleChangeMode" /> | ||
| 17 | + </section> | ||
| 18 | +</template> |