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 | 2 | import { |
| 3 | 3 | TDeviceConfigParams, |
| 4 | 4 | IDeviceConfigAddOrEditModel, |
| 5 | + ProfileRecord, | |
| 5 | 6 | } from '/@/api/device/model/deviceConfigModel'; |
| 7 | +import { PaginationResult } from '/#/axios'; | |
| 6 | 8 | |
| 7 | 9 | enum EDeviceConfigApi { |
| 8 | 10 | /** |
| ... | ... | @@ -52,7 +54,7 @@ export const alarmContactGetPage = () => { |
| 52 | 54 | * 分页查询设备配置页面 |
| 53 | 55 | */ |
| 54 | 56 | export const deviceConfigGetQuery = (params?: TDeviceConfigParams) => { |
| 55 | - return defHttp.get({ | |
| 57 | + return defHttp.get<PaginationResult<ProfileRecord>>({ | |
| 56 | 58 | url: EDeviceConfigApi.DEVICE_CONFIG_GET_PAGE, |
| 57 | 59 | params, |
| 58 | 60 | }); | ... | ... |
| ... | ... | @@ -199,3 +199,45 @@ export interface AlarmLogItem { |
| 199 | 199 | organizationId: string; |
| 200 | 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 | 9 | import { h } from 'vue'; |
| 10 | 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 | 23 | export enum ModelOfMatterPermission { |
| 13 | 24 | CREATE = 'api:yt:things_model:post', |
| 14 | 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 | 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 | 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> | ... | ... |