diff --git a/src/api/device.ts b/src/api/device.ts new file mode 100644 index 0000000..d4ceacb --- /dev/null +++ b/src/api/device.ts @@ -0,0 +1,82 @@ +import axios from 'axios'; + +export interface DeviceRecord { + size: number; + current: number; + name?: string; + clientId?: string; + productId?: number; + status?: string; + isOnline?: boolean; + pageable?: string; +} + +export interface DeviceCreateRecord { + num?: number; + prefix?: string; + name?: string; + hardwareVersion: string; + firmwareVersion: string; + extendParams: string; + properties: string; + productId: number; +} + +export interface DeviceEventRecord { + id: number; + clientId: string; + serveName: string; + params: string; +} + +// 分页查询 +export function queryDeviceList(data: DeviceRecord) { + return axios({ + url: '/api/rest/device', + method: 'get', + params: data, + }); +} + +// 查看详情 +export function queryDeviceDetail(id: number) { + return axios.get(`/api/rest/device/${id}`); +} + +// 新增 +export function createDevice(data: DeviceCreateRecord) { + return axios.post(`/api/rest/device`, data); +} + +// 修改 +export function updateDevice(id: number, data: DeviceCreateRecord) { + return axios.put(`/api/rest/device/${id}`, data); +} +// 删除 +export function deleteDevice(id: number) { + return axios.delete(`/api/rest/device/${id}`); +} + +// 查询上报 +export function queryDeviceReport(clientId: number) { + return axios({ + url: `/api/rest/device/record/photo`, + method: 'get', + params: clientId, + }); +} + +// 批量创建 +export function createDeviceBatch(data: DeviceCreateRecord) { + return axios.post(`/api/rest/device/batch`, data); +} + +// 手动触发事件 +export function triggerEvent(data: DeviceEventRecord) { + return axios({ + url: `/api/rest/device/event`, + method: 'post', + data, + }); +} + diff --git a/src/api/message.ts b/src/api/message.ts index e2d60ee..ff4cbd2 100644 --- a/src/api/message.ts +++ b/src/api/message.ts @@ -18,9 +18,10 @@ export function queryMessageList() { } interface MessageStatus { - ids: number[]; + ids: string[]; } +// 批量设置消息已读 export function setMessageStatus(data: MessageStatus) { return axios.post<MessageListType>('/api/message/read', data); } diff --git a/src/api/product.ts b/src/api/product.ts new file mode 100644 index 0000000..3a841e0 --- /dev/null +++ b/src/api/product.ts @@ -0,0 +1,63 @@ +import axios from 'axios'; + +export interface ProductRecord { + size: number; + current: number; + name?: string; + pageable?: string; + type?: string; + links?: string; +} + +export interface ProductCreateRecord { + name: string; + model: string; + type?: string; + link?: string; + remark?: string; + params:[{ + name: string; + identifier: string; + type: string; + dataType: string; + }]; +} + + +// 分页查询 +export function queryProductList(data: ProductRecord) { + return axios({ + url: '/api/rest/product', + method: 'get', + params: data, + }); +} +// 模糊查询获取产品列表 +export function queryProductListAll(data: ProductRecord) { + return axios({ + url: '/api/rest/product/fuzzy', + method: 'get', + params: data, + }); +} + +// 查看详情 +export function queryProductDetail(id: number) { + return axios.get(`/api/rest/product/${id}`); +} + +// 新增 +export function createProduct(data: ProductCreateRecord) { + return axios.post(`/api/rest/product`, data); +} + +// 修改 +export function updateProduct(id: number, data: ProductCreateRecord){ + return axios.patch(`/api/rest/product/${id}`, data); +} + +// 删除 + export function deleteProduct(id: number) { + return axios.delete(`/api/rest/product/${id}`); + } + diff --git a/src/mock/index.ts b/src/mock/index.ts index ae390f6..97d9cc4 100644 --- a/src/mock/index.ts +++ b/src/mock/index.ts @@ -5,21 +5,10 @@ import './message-box'; import '@/views/dashboard/workplace/mock'; -import '@/views/dashboard/monitor/mock'; -import '@/views/list/card/mock'; -import '@/views/list/search-table/mock'; - -import '@/views/form/step/mock'; - -import '@/views/profile/basic/mock'; - -import '@/views/visualization/data-analysis/mock'; -import '@/views/visualization/multi-dimension-data-analysis/mock'; - -import '@/views/user/info/mock'; import '@/views/user/setting/mock'; + Mock.setup({ timeout: '600-1000', }); diff --git a/src/router/routes/modules/iot.ts b/src/router/routes/modules/iot.ts new file mode 100644 index 0000000..d5067ef --- /dev/null +++ b/src/router/routes/modules/iot.ts @@ -0,0 +1,41 @@ +import { DEFAULT_LAYOUT } from '../base'; +import { AppRouteRecordRaw } from '../types'; + +const IOT: AppRouteRecordRaw = { + path: '/iot', + name: 'iot', + component: DEFAULT_LAYOUT, + meta: { + locale: 'menu.iot', + title: '物联网管理', + icon: 'icon-empty', + requiresAuth: true, + order: 1, + }, + children: [ + { + path: 'device', + name: 'Device', + component: () => import('@/views/iot/device/index.vue'), + meta: { + // locale: 'menu.system.role', + title: '设备管理', + requiresAuth: true, + permissions: ['*'], + }, + }, + { + path: 'product', + name: 'Product', + component: () => import('@/views/iot/product/index.vue'), + meta: { + // locale: 'menu.system.dept', + title: '产品管理', + requiresAuth: true, + permissions: ['*'], + }, + }, + ], +}; + +export default IOT; diff --git a/src/router/routes/modules/notification.ts b/src/router/routes/modules/notification.ts deleted file mode 100644 index fa81e16..0000000 --- a/src/router/routes/modules/notification.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { DEFAULT_LAYOUT } from '../base'; -import { AppRouteRecordRaw } from '../types'; - -const NOTIFICATION: AppRouteRecordRaw = { - path: '/notification', - name: 'notification', - component: DEFAULT_LAYOUT, - meta: { - locale: '通知管理', - requiresAuth: true, - icon: 'icon-message', // 设置图标 - order: 0, // 排序路由菜单项。如果设置该值,值越高,越靠前 - }, - children: [ - { - path: 'notice', - name: 'notice', - component: () => import('@/views/notification/notice/index.vue'), - meta: { - locale: '公告通知', - requiresAuth: true, - permissions: ['*'], - }, - }, - { - path:'noticeSet', - name:'noticeSet', - component: () => import('@/views/notification/noticeSet/index.vue'), - meta:{ - locale: '公告管理', - requiresAuth: true, - permissions: ['*'], - } - } - ], -}; -export default NOTIFICATION; - diff --git a/src/router/routes/modules/system.ts b/src/router/routes/modules/system.ts index 201eb0b..681bcb4 100644 --- a/src/router/routes/modules/system.ts +++ b/src/router/routes/modules/system.ts @@ -27,9 +27,10 @@ const SYSTEM: AppRouteRecordRaw = { name: 'Role', component: () => import('@/views/system/role/index.vue'), meta: { - locale: 'menu.system.role', + // locale: 'menu.system.role', + title: '角色管理', requiresAuth: true, - permissions: ['admin'], + permissions: ['*'], }, }, { @@ -37,9 +38,10 @@ const SYSTEM: AppRouteRecordRaw = { name: 'Dept', component: () => import('@/views/system/dept/index.vue'), meta: { - locale: 'menu.system.dept', + // locale: 'menu.system.dept', + title: '部门管理', requiresAuth: true, - permissions: ['admin'], + permissions: ['*'], }, }, { @@ -47,19 +49,52 @@ const SYSTEM: AppRouteRecordRaw = { name: 'User', component: () => import('@/views/system/user/index.vue'), meta: { - locale: 'menu.system.user', + // locale: 'menu.system.user', + title: '用户管理', requiresAuth: true, - permissions: ['admin'], + permissions: ['*'], }, }, { path: 'authority', - name: 'authority', + name: 'Authority', component: () => import('@/views/system/authority/index.vue'), meta: { - locale: '权限管理', + // locale: '权限管理', + title: '权限管理', requiresAuth: true, - permissions: ['admin'], + permissions: ['*'], + }, + }, + { + path:'bulletin', + name:'Bulletin', + component: () => import('@/views/system/bulletin/index.vue'), + meta:{ + title: '公告管理', + requiresAuth: true, + permissions: ['*'], + }, + }, + { + path:'message', + name:'Message', + component: () => import('@/views/system/message/index.vue'), + meta:{ + title: '消息管理', + requiresAuth: true, + permissions: ['*'], + }, + }, + { + path: 'detail/:id', + name: 'Detail', + component: () => import('@/views/system/bulletin/components/detail.vue'), + meta: { + title: '公告详情', + requiresAuth: true, + showInMenu: false, + permissions: ['*'], }, }, ], diff --git a/src/store/modules/auth/index.ts b/src/store/modules/auth/index.ts index a663423..a56a24a 100644 --- a/src/store/modules/auth/index.ts +++ b/src/store/modules/auth/index.ts @@ -46,9 +46,6 @@ import { async updateAuth(params: AuthRecord) { return update(params); }, - async getAuthDetail(id: string) { - return getDetail(id); - }, }, }); diff --git a/src/views/iot/device/components/device-edit.vue b/src/views/iot/device/components/device-edit.vue new file mode 100644 index 0000000..368b786 --- /dev/null +++ b/src/views/iot/device/components/device-edit.vue @@ -0,0 +1,249 @@ +<template> + <a-button v-if="props.isCreate" type="primary" @click="handleClick"> + <template #icon><icon-plus /></template> + 新建 + </a-button> + <a-button + v-if="!props.isCreate" + type="outline" + size="small" + :style="{ marginRight: '10px', padding: '7px' }" + @click="handleClick" + > + <template #icon><icon-edit /></template> + 修改 + </a-button> + + <a-modal + width="900px" + :visible="visible" + @cancel="handleCancel" + > + <template #title>{{ modalTitle }}</template> + <DynamicForm :prem="formSystem" /> + + <template #footer> + <a-button class="editor-button" @click="handleCancel">取消</a-button> + <a-button class="editor-button" type="primary" @click="handleSubmit">确定</a-button> + </template> + </a-modal> +</template> + +<script lang="ts" setup> + import useVisible from '@/hooks/visible'; + import { computed, defineEmits, PropType, ref, shallowRef, onBeforeUnmount, reactive } from 'vue'; + import { CreateRecord } from '@/api/user'; + import { FormInstance } from '@arco-design/web-vue/es/form'; + import { Message } from '@arco-design/web-vue'; + import { useMessageStore } from '@/store'; + import DynamicForm from '@/components/dynamic-form/index.vue'; + import { Editor, Toolbar } from '@wangeditor/editor-for-vue'; + import { IEditorConfig } from '@wangeditor/editor' + import '@wangeditor/editor/dist/css/style.css' + + const props = defineProps({ + prem: { + type: Object as PropType<CreateRecord>, + }, + isCreate: Boolean, + }); + // 部门树模态框状态 + const deptVisible=ref(false); + const emit = defineEmits(['refresh']); + const modalTitle = computed(() => { + return props.isCreate ? '创建设备' : '编辑设备'; + }); + const { visible, setVisible } = useVisible(false); + const checkKeys = ref<number[]>([]); + + const CreateRef = ref<FormInstance>(); + const formData = ref<any>({ + ...props.prem, + }); + const messageStore = useMessageStore(); + const editorRef = shallowRef() + const selectedIds= ref<string[]>([]); + const columns = computed<any[]>(()=>[ + { + title: '操作', + dataIndex: 'key', + slotName: 'key', + }, + { + title: '用户', + dataIndex: 'title', + }, + ]) + + const formSystem = ref( + { + productId:{ + label: '产品ID', + component: 'input', + type:'text' + }, + name:{ + label: '设备名称', + component: 'input', + type:'text' + }, + hardwareVersion:{ + label: '硬件版本', + component: 'input', + type:'text' + }, + firmwareVersion:{ + label: '固件版本', + component: 'input', + type:'text' + }, + extendParams:{ + label: '扩展属性', + component: 'input', + type:'text' + }, + properties:{ + label: '设备物模型属性', + component: 'input', + type:'text' + }, + }, + ); + const deptTreeData = reactive([ + { + key: '1', + title: '总部门', + children: [ + { + key: '2', + title: '部门1', + members: [ + { key: '101', title: '成员1' }, + { key: '102', title: '成员2' } + ] + }, + { + key: '3', + title: '部门2', + members: [ + { key: '201', title: '成员3' }, + { key: '202', title: '成员4' } + ] + }, + { + key: '4', + title: '部门3', + members: [ + { key: '203', title: '成员5' }, + { key: '204', title: '成员6' } + ] + } + ] + } + ]); + const selectedDepartmentMembers: any = ref([]); + const renderData = ref<any[]>([]); + // 部门树查询 + const queryDeptTree= async ()=>{ + deptVisible.value = true; + } + + // 广度优先遍历树,获取选中的成员 + const getSelectedMembers = (treeData: any[], selectedKeys: string[]) => { + const queue = [...treeData]; + const selectedMembers: any[] = []; + while (queue.length > 0) { + const node = queue.shift()!; + if (selectedKeys.includes(node.key)) { + if (node.members) { + selectedMembers.push(...node.members); + } + } + if (node.children) { + queue.push(...node.children); + } + } + return selectedMembers; + } + + // 组件被点击 + const handleClick = () => { + setVisible(true); + }; + + const editorConfig: Partial<IEditorConfig> = { placeholder: '请输入内容...',MENU_CONF:{ + // 隐藏菜单 + hide: ['code', 'table', 'emoticon', 'uploadImage', 'video', 'todo', 'specialChar'], + // 配置上传图片 + uploadImage: { + base64LimitSize: 1024 * 1024, + // server: '/api/rest/bulletin/1/add', + } + } + } + // 提交 + const handleSubmit = async () => { + const valid = await CreateRef.value?.validate(); + if (!valid) { + formData.value.permissionIds = checkKeys.value; + // 新增 + if (props.isCreate) { + // formData.value.username = formData.value.email; + const res = await messageStore.createMessage(formData.value); + if (res.status === 200) { + Message.success({ + content: '新建成功', + duration: 5 * 1000, + }); + emit('refresh'); + setVisible(false); + } + CreateRef.value?.resetFields(); + } + } + }; + + // 组件销毁时,也及时销毁编辑器 + onBeforeUnmount(() => { + const editor = editorRef.value + if (editor == null) return + editor.destroy() + }) + + // 关闭 + const handleCancel = async () => { + checkKeys.value = []; + setVisible(false); + }; + + // 设备模态框提交 + const deptTreeSubmit = () => { + deptVisible.value = false; + formData.value.userIds = selectedIds.value; + console.log(formData.value.userIds); + }; +</script> + +<style scoped> + .fullscreen-toolbar { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 50px; /* 根据需要调整高度 */ + z-index: 999; + background-color: white; + } + .fullscreen-editor { + position: fixed; + top: 50px; /* 根据 toolbar 的高度调整 */ + left: 0; + width: 100%; + height: calc(100% - 50px); /* 减去 toolbar 的高度 */ + z-index: 999; + background-color: white; + } + .editor-button{ + position: static + } +</style> diff --git a/src/views/iot/device/index.vue b/src/views/iot/device/index.vue new file mode 100644 index 0000000..76dee0e --- /dev/null +++ b/src/views/iot/device/index.vue @@ -0,0 +1,374 @@ +<template> + <div class="container"> + <Breadcrumb :items="['系统管理', '公告设置']" /> + <a-card class="general-card" title=" "> + <a-row> + <a-col :flex="1"> + <a-form + :model="formModel" + :label-col-props="{ span: 6 }" + :wrapper-col-props="{ span: 18 }" + label-align="right" + > + <a-row :gutter="18"> + <a-col :span="9"> + <a-form-item + field="name" + label='名称' + > + <a-input + v-model="formModel.name" + style="width: 360px" + placeholder='请输入设备名称' + /> + </a-form-item> + </a-col> + <a-col :span="10"> + <a-form-item field="status" label='状态'> + <a-input + v-model="formModel.status" + style="width: 360px" + placeholder='请输入设备状态' + /> + </a-form-item> + </a-col> + <a-col :span="9"> + <a-form-item + field="isOnline" + label='在线' + > + <a-select + v-model="formModel.isOnline" + style="width: 360px" + placeholder='请选择是否在线' + :options="statusOptions" + /> + </a-form-item> + </a-col> + + + + </a-row> + </a-form> + </a-col> + <a-divider style="height: 84px" direction="vertical" /> + <a-col :flex="'86px'" style="text-align: right"> + <a-space direction="vertical" :size="18"> + <a-button type="primary" @click="search"> + <template #icon> + <icon-search /> + </template> + 查询 + </a-button> + <a-button @click="reset"> + <template #icon> + <icon-refresh /> + </template> + 重置 + </a-button> + </a-space> + </a-col> + </a-row> + <a-divider style="margin-top: 0" /> + <a-row> + <a-col :span="12"> + <a-space> + <DeviceEdit ref="createUserRef" :is-create="true" @refresh="search" /> + </a-space> + </a-col> + <a-col + :span="12" + style=" + display: flex; + align-items: center; + justify-content: end; + padding-bottom: 20px; + " + > + <a-tooltip content='刷新'> + <div class="action-icon" @click="search"> + <icon-refresh size="18" /> + </div> + </a-tooltip> + + <a-dropdown @select="handleSelectDensity"> + <a-tooltip content='密度'> + <div class="action-icon"><icon-line-height size="18" /></div> + </a-tooltip> + <template #content> + <a-doption + v-for="item in densityList" + :key="item.value" + :value="item.value" + :class="{ active: item.value === size }" + > + <span>{{ item.name }}</span> + </a-doption> + </template> + </a-dropdown> + + <a-tooltip content='列设置'> + <a-popover + trigger="click" + position="bl" + @popup-visible-change="popupVisibleChange" + > + <div class="action-icon"><icon-settings size="18" /></div> + <template #content> + <div id="tableSetting"> + <div + v-for="(item, index) in showColumns" + :key="item.dataIndex" + class="setting" + > + <div style="margin-right: 4px; cursor: move"> + <icon-drag-arrow /> + </div> + <div> + <a-checkbox + v-model="item.checked" + @change=" + handleChange($event, item as TableColumnData, index) + " + > + </a-checkbox> + </div> + <div class="title"> + {{ item.title === '#' ? '序列号' : item.title }} + </div> + </div> + </div> + </template> + </a-popover> + </a-tooltip> + </a-col> + </a-row> + + <a-table + row-key="id" + :loading="loading" + :pagination="false" + :columns="(cloneColumns as TableColumnData[])" + :data="renderData" + :bordered="false" + :size="size" + style="margin-bottom: 40px" + @page-change="onPageChange" + > + <template #id="{ record }"> + <span>{{ record.id }}</span> + </template> + <template #online="{ record }"> + {{ record.online == true? '是' : '否' }} + </template> + <template #operations="{ record }"> + <a-button + type="outline" + size="small" + status="success" + style="padding: 7px; margin-right: 10px" + @click="openDetail(record.id)" + > + <template #icon><icon-list /></template> + 详情 + </a-button> + </template> + </a-table> + <a-pagination + style="float: right; position: relative; right: 1px; bottom: 25px" + :total="pagination.total" + :size="size" + show-total + show-jumper + show-page-size + @page-size-change="onSizeChange" + @change="onPageChange" + /> + </a-card> + </div> +</template> + +<script lang="ts" setup> + import { computed, onMounted, ref, watch } from 'vue'; + import useLoading from '@/hooks/loading'; + import usePagination from '@/hooks/pagination'; + import type { SelectOptionData } from '@arco-design/web-vue/es/select/interface'; + import type { TableColumnData } from '@arco-design/web-vue/es/table/interface'; + import { useBulletinStore } from '@/store'; + import useTableOption from '@/hooks/table-option'; + import { Message } from '@arco-design/web-vue'; + import { useRouter } from 'vue-router'; + import { DeviceRecord, queryDeviceList } from '@/api/device'; + import DeviceEdit from '@/views/iot/device/components/device-edit.vue'; + + const generateFormModel = () => { + return { + name:'', + status:'', + isOnline:'', + }; + }; + + const { loading, setLoading } = useLoading(true); + const { pagination,setPagination } = usePagination(); + const renderData = ref<[]>([]); + const formModel = ref(generateFormModel()); + const router = useRouter(); + const { + cloneColumns, + showColumns, + densityList, + size, + handleSelectDensity, + handleChange, + popupVisibleChange, + deepClone, + } = useTableOption(); + + const bulletinStore = useBulletinStore(); + + const columns = computed<TableColumnData[]>(() => [ + { + title: 'ID', + dataIndex: 'id', + slotName: 'id', + width: 60, + }, + { + title: '名称', + dataIndex: 'name', + }, + { + title: '状态', + dataIndex: 'state', + slotName: 'state', + }, + { + title: '在线状态', + dataIndex: 'online', + slotName:'online', + sortable: { + sortDirections: ['ascend', 'descend'], + }, + }, + { + title: '操作', + dataIndex: 'operations', + slotName: 'operations', + }, + ]); + const statusOptions = computed<SelectOptionData[]>(() => [ + { + label: '是', + value: 'true', + }, + { + label: '否', + value: 'false', + }, + ]); + + // 获取设备列表 + const fetchData = async ( + params = { size: 10, current: 1 } + ) => { + setLoading(true); + try { + const res: any = await queryDeviceList(params); + renderData.value = res.data.records; + setPagination(res.data); + } catch (err) { + // you can report use errorHandler or other + } finally { + setLoading(false); + } + }; + + // 查询 + const search = () => { + fetchData({ + ...pagination, + ...formModel.value, + } as unknown as DeviceRecord); + }; + + // 分页发生改变 + const onPageChange = (current: number) => { + pagination.page = current; + pagination.current = current; + search(); + }; + + // 数据条数改变 + const onSizeChange = (Size: number) => { + pagination.size = Size; + search(); + }; + + + // 重置 + const reset = () => { + formModel.value = generateFormModel(); + }; + + // 打开详情 + function openDetail(id:number): void{ + const url = router.resolve({ + name: 'Detail', + params: {id} + }).href; + router.push(url); + }; + + // 删除 + const handleDelete = async (id: number) => { + const res = await bulletinStore.removeBulletin(id); + if (res.status === 200) { + Message.success({ + content: '删除成功', + duration: 5 * 1000, + }); + search(); + } + }; + onMounted(() => { + search(); + }); + watch(() => columns.value, deepClone, { deep: true, immediate: true }); +</script> + + +<style scoped lang="less"> + .container { + padding: 0 20px 20px 20px; + } + + :deep(.arco-table-th) { + &:last-child { + .arco-table-th-item-title { + margin-left: 16px; + } + } + } + + .action-icon { + margin-left: 12px; + cursor: pointer; + } + + .active { + color: #0960bd; + background-color: #e3f4fc; + } + + .setting { + display: flex; + align-items: center; + width: 200px; + + .title { + margin-left: 12px; + cursor: pointer; + } + } +</style> \ No newline at end of file diff --git a/src/views/iot/product/index.vue b/src/views/iot/product/index.vue new file mode 100644 index 0000000..e69de29 diff --git a/src/views/login/components/login-form.vue b/src/views/login/components/login-form.vue index c0a4df1..b3a8aeb 100644 --- a/src/views/login/components/login-form.vue +++ b/src/views/login/components/login-form.vue @@ -1,6 +1,6 @@ <template> <div class="login-form-wrapper"> - <div class="login-form-title">{{ $t('login.form.title') }}</div> + <div class="login-form-title">物联网网关系统</div> <!-- <div class="login-form-sub-title">{{ $t('login.form.title') }}</div>--> <!-- <div class="login-form-sub-title">请先登录</div>--> <div class="login-form-error-msg">{{ errorMessage }}</div> diff --git a/src/views/notification/noticeSet/components/notice-edit.vue b/src/views/notification/noticeSet/components/notice-edit.vue deleted file mode 100644 index 368fda7..0000000 --- a/src/views/notification/noticeSet/components/notice-edit.vue +++ /dev/null @@ -1,214 +0,0 @@ -<template> - <a-button v-if="props.isCreate" type="primary"> - <template #icon><icon-plus /></template> - 新建 - </a-button> - <a-button - v-if="!props.isCreate" - type="outline" - size="small" - :style="{ marginRight: '10px', padding: '7px' }" - > - <template #icon><icon-edit /></template> - 修改 - </a-button> - - <a-modal - width="700px" - :visible="visible" - @ok="handleSubmit" - @cancel="handleCancel" - > - <template #title>{{ modalTitle }}</template> - <a-form ref="CreateRef" :model="formData" :style="{ width: '650px' }"> - <a-form-item - field="title" - :label="$t('标题')" - :validate-trigger="['change', 'input']" - :rules="[ - { required: true, message: '' }, - { - match: /^[a-zA-Z0-9\u4e00-\u9fa5]{1,20}$/, - message: '', - }, - ]" - > - <a-input - v-model="formData.title" - :placeholder="$t('请输入公告标题')" - /> - <!-- v-if="props.isCreate"--> -<!-- <div v-else>{{ formData.title }}</div>--> - </a-form-item> - <a-form-item - field="email" - :label="$t('作者')" - :rules="[ - { - required: true, - type: 'email', - message: t('user.info.email.required'), - }, - ]" - :validate-trigger="['change', 'input']" - > - <a-input - v-model="formData.email" - :placeholder="$t('请输入作者')" - /> - </a-form-item> - <a-form-item - field="phone" - :label="$t('发布范围')" - :rules="[ - { required: true, message: t('user.info.phone.required') }, - { match: /^1[3-9]\d{9}$/, message: t('user.info.phone.format') }, - ]" - :validate-trigger="['change', 'input']" - > - <a-input - v-model="formData.phone" - :placeholder="$t('请选择范围')" - /> - </a-form-item> - <a-form-item - field="password" - :label="$t('发布时间')" - :validate-trigger="['change', 'input']" - :rules="[{ required: true, message: t('user.info.password.required') }]" - > - <!-- v-if="isCreate"--> - <a-input - v-model="formData.password" - :placeholder="$t('请选择发布时间')" - /> - </a-form-item> - <a-form-item field="nickName" :label="$t('公告内容')"> - <a-textarea default-value="请输入内容" :auto-size="{ - minRows:2, - maxRows:5 - }" style="margin-top: 20px" v-model="formData.content"/> - </a-form-item> - </a-form> - </a-modal> -</template> - -<script lang="ts" setup> -import { useI18n } from 'vue-i18n'; -import useVisible from '@/hooks/visible'; -import { computed, PropType, ref } from 'vue'; -import { CreateRecord } from '@/api/user'; -import { FormInstance } from '@arco-design/web-vue/es/form'; -import { deptList } from '@/api/dept'; -import { Message } from '@arco-design/web-vue'; -import { useUserStore } from '@/store'; - -const props = defineProps({ - prem: { - type: Object as PropType<CreateRecord>, - }, - isCreate: Boolean, -}); -const { t } = useI18n(); -const modalTitle = computed(() => { - return props.isCreate ? t('新增公告') : t('编辑公告'); -}); -const { visible, setVisible } = useVisible(false); -const checkKeys = ref<number[]>([]); - -const CreateRef = ref<FormInstance>(); - -const formData = ref<any>({ - -}); - -let formDifer = {}; -const userStore = useUserStore(); - -// 部门数据 -const deptOptions = ref(); -const getDeptData = async () => { - const res = await deptList(); - deptOptions.value = res.data.records; -}; -// 角色数据 -// const roleOptions = ref(); -// const getRoleData = async () => { -// const res = await queryRoleList(''); -// roleOptions.value = res.data.records.filter((item: any) => { -// return item.enabled !== false; -// }); -// }; - -// 组件被点击 -// const handleClick = () => { -// getDeptData(); -// // getRoleData(); -// const userId = props.prem?.id; -// // 编辑 -// if (!props.isCreate && userId) { -// formData.value = props.prem; -// formDifer = { ...props.prem }; -// } -// setVisible(true); -// }; - -// 做出只修改的部分 -const diffDataForm = (newData: any, oldData: any) => { - const result = {}; // 报错修改的字段内容 - Object.keys(oldData).forEach((key) => { - if (oldData[key] !== newData[key]) { - result[key] = newData[key]; - } - }); - return result; -}; - -// 提交 -const handleSubmit = async () => { - const valid = await CreateRef.value?.validate(); - if (!valid) { - formData.value.permissionIds = checkKeys.value; - // 新增 - if (props.isCreate) { - // formData.value.username = formData.value.email; - const res = await userStore.createUser(formData.value); - if (res.status === 200) { - Message.success({ - content: t('create.sucess'), - duration: 5 * 1000, - }); - } - CreateRef.value?.resetFields(); - } else { - // 编辑 - formDifer = diffDataForm(formData.value, formDifer); - if (Object.keys(formDifer).length === 0) { - Message.success({ - content: t('unmodified'), - duration: 3 * 1000, - }); - } else { - formDifer.id = formData.value.id; - const res = await userStore.updateUser(formDifer); - if (res.status === 200) { - Message.success({ - content: t('modify.sucess'), - duration: 5 * 1000, - }); - } - } - } - checkKeys.value = []; - setVisible(false); - } -}; - -// 关闭 -const handleCancel = async () => { - checkKeys.value = []; - setVisible(false); -}; -</script> - -<style scoped></style> diff --git a/src/views/system/role/components/role-table.vue b/src/views/system/role/components/role-table.vue deleted file mode 100644 index ddfaf6e..0000000 --- a/src/views/system/role/components/role-table.vue +++ /dev/null @@ -1,132 +0,0 @@ -<template> - <a-table row-key="id" :loading="loading" :pagination="false" :columns="columns" :data="data" :bordered="false" - :size="size" style="margin-bottom: 40px" @pageChange="onPageChange"> - <template #index="{ rowIndex }"> - {{ rowIndex + 1 }} - </template> - <template #createTime="{ record }"> - {{ dayjs(record.createTime).format('YYYY-MM-DD') }} - </template> - <template #enabled="{ record }"> - <a-switch :model-value="record.enabled" :checked-value="true" :unchecked-value="false" - @change="enabledStatus(record)" /> - </template> - <template #operations="{ record }"> - <!-- 编辑 --> - <RoleEdit ref="editRef" :prem="record" :is-create="false" @refresh="fetchData" /> - <a-popconfirm :content="t('Confirm the deletion of this role')" type="error" @ok="handleDelete(record)"> - <a-button type="outline" size="small" status="danger" style="padding: 7px"> - <template #icon><icon-delete /></template> - 删除 - </a-button> - </a-popconfirm> - </template> - </a-table> -</template> - -<script lang="ts" setup> -import { defineProps, defineEmits} from 'vue'; -import dayjs from 'dayjs'; -import { useI18n } from 'vue-i18n'; -import { Message } from '@arco-design/web-vue'; -import { useRoleStore } from '@/store'; -import { RoleRecord } from '@/api/role'; -// import type { TableColumnData } from '@arco-design/web-vue/es/table/interface'; -import RoleEdit from './role-edit.vue'; - -const roleStore = useRoleStore(); -const props = defineProps({ - loading: Boolean, - data: Array, - size: String, -}); - -const { t } = useI18n(); -const emit = defineEmits(['pageChange', 'refresh','search']); -// 表格头部 -// const columns = computed<TableColumnData[]>(() => [ -// { -// title: t('roleTable.columns.index'), -// dataIndex: 'index', -// slotName: 'index', -// }, -// { -// title: t('roleTable.columns.name'), -// dataIndex: 'name', -// sortable: { -// sortDirections: ['ascend', 'descend'], -// }, -// }, -// { -// title: t('roleTable.columns.remark'), -// dataIndex: 'remark', -// }, -// { -// title: t('roleTable.columns.createTime'), -// dataIndex: 'createTime', -// slotName: 'createTime', -// sortable: { -// sortDirections: ['ascend', 'descend'], -// }, -// }, -// { -// title: t('roleTable.columns.enabled'), -// dataIndex: 'enabled', -// slotName: 'enabled', -// }, -// { -// title: t('searchTable.columns.operations'), -// dataIndex: 'operations', -// slotName: 'operations', -// }, -// ]); - - -const fetchData = () => { - emit('refresh'); -}; -// 分页 -const onPageChange = (page: number) => { - emit('pageChange', page); -}; - -// 是否启用 -const enabledStatus = async (record: string) => { - record.enabled = !record.enabled; - const res = await roleStore.enabledRole(record.id); - if (res.status === 200) { - Message.success({ - content: t('modify.status.sucess'), - duration: 3 * 1000, - }); - } else { - Message.error({ - content: t('modify.status.fail'), - duration: 3 * 1000, - }); - } -}; - -// 删除 -const handleDelete = async (record: RoleRecord) => { - const res = await roleStore.removeRole(record.id); - if (res.status === 200) { - Message.success({ - content: t('delete.role.sucess'), - duration: 5 * 1000, - }); - emit('search'); - } else { - Message.error({ - content: t('delete.role.fail'), - duration: 3 * 1000, - }); - } -}; - -// watch(() => columns.value, deepClone, { deep: true, immediate: true }); -</script> - -<style scoped> -/* 添加一些样式 */ -</style> \ No newline at end of file