diff --git a/src/api/device/instance.ts b/src/api/device/instance.ts index 9f051caf..19df86c2 100644 --- a/src/api/device/instance.ts +++ b/src/api/device/instance.ts @@ -1,6 +1,7 @@ +import { LocalStore } from '@/utils/comm' import server from '@/utils/request' -import { BASE_API_PATH } from '@/utils/variable' -import { DeviceInstance } from '@/views/device/instance/typings' +import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable' +import { DeviceInstance } from '@/views/device/Instance/typings' /** * 删除设备物模型 @@ -97,5 +98,5 @@ export const batchDeleteDevice = (data: string[]) => server.put(`/device-instanc * @param type 文件类型 * @returns */ - export const deviceExport = (productId: string, type: string) => `${BASE_API_PATH}/device-instance${!!productId ? '/' + productId : ''}/export.${type}` +export const deviceExport = (productId: string, type: string) => `${BASE_API_PATH}/device-instance${!!productId ? '/' + productId : ''}/export.${type}` diff --git a/src/api/device/product.ts b/src/api/device/product.ts index 6f318dcf..11c70020 100644 --- a/src/api/device/product.ts +++ b/src/api/device/product.ts @@ -42,4 +42,11 @@ export const detail = (id: string) => server.get(`/device-product/$ * 产品分类 * @param data */ -export const category = (data: any) => server.post('/device/category/_tree', data) \ No newline at end of file +export const category = (data: any) => server.post('/device/category/_tree', data) + +/** + * 保存产品 + * @param data 产品信息 + * @returns + */ +export const saveProductMetadata = (data: Record) => server.patch('/device-product', data) \ No newline at end of file diff --git a/src/api/home.ts b/src/api/home.ts index 2164e2f5..a37c0f3d 100644 --- a/src/api/home.ts +++ b/src/api/home.ts @@ -12,4 +12,6 @@ export const getDeviceCount_api = () => server.get(`/device/instance/_count`); // 产品数量 export const getProductCount_api = (data:object) => server.post(`/device-product/_count`, data); // 查询产品列表 -export const getProductList_api = (data:object) => server.get(`/device/product/_query/no-paging?paging=false`, data); +export const getProductList_api = (data:object={}) => server.get(`/device/product/_query/no-paging?paging=false`, data); +// 查询设备列表 +export const getDeviceList_api = (data:object) => server.post(`/device-instance/_query/`, data); diff --git a/src/api/system/permission.ts b/src/api/system/permission.ts index 978e4b26..659abbed 100644 --- a/src/api/system/permission.ts +++ b/src/api/system/permission.ts @@ -1,6 +1,15 @@ import server from '@/utils/request'; // 获取权限列表 -export const getPermission_api = (data:object) => server.post(`/permission/_query/`,data); -// 修改权限信息 -export const editPermission_api = (data:object) => server.patch(`/permission`,data); \ No newline at end of file +export const getPermission_api = (data: object) => server.post(`/permission/_query/`, data); +// 新增时校验标识id是否可用 +export const checkId_api = (data: object) => server.get(`/permission/id/_validate`, data); +// 修改权限 | 导入文件内容 +export const editPermission_api = (data: object) => server.patch(`/permission`, data); +// 添加权限 +export const addPermission_api = (data: object) => server.post(`/permission`, data); +// 删除权限 +export const delPermission_api = (id: string) => server.remove(`/permission/${id}`); + +// 导出权限数据 +export const exportPermission_api = (data: object) => server.post(`/permission/_query/no-paging`, data); diff --git a/src/components/Table/index.tsx b/src/components/Table/index.tsx index 92069199..7c43d1f7 100644 --- a/src/components/Table/index.tsx +++ b/src/components/Table/index.tsx @@ -54,7 +54,7 @@ export interface JTableProps extends TableProps{ rowSelection?: TableProps['rowSelection']; cardProps?: Record; dataSource?: Record[]; - gridColumn: number; + gridColumn?: number; /** * 用于不同分辨率 * gridColumns[0] 1366 ~ 1440 分辨率; diff --git a/src/store/instance.ts b/src/store/instance.ts index f4d6e145..eb51df72 100644 --- a/src/store/instance.ts +++ b/src/store/instance.ts @@ -1,4 +1,4 @@ -import { DeviceInstance, InstanceModel } from "@/views/device/instance/typings"; +import { DeviceInstance, InstanceModel } from "@/views/device/Instance/typings" import { defineStore } from "pinia"; export const useInstanceStore = defineStore({ @@ -7,6 +7,7 @@ export const useInstanceStore = defineStore({ actions: { setCurrent(current: Partial) { this.current = current + this.detail = current } } }) \ No newline at end of file diff --git a/src/store/metadata.ts b/src/store/metadata.ts new file mode 100644 index 00000000..c793f233 --- /dev/null +++ b/src/store/metadata.ts @@ -0,0 +1,31 @@ +import { DeviceInstance, InstanceModel } from "@/views/device/Instance/typings" +import { defineStore } from "pinia"; +import type { MetadataItem, MetadataType } from '@/views/device/Product/typings' + +type MetadataModelType = { + item: MetadataItem | unknown; + edit: boolean; + type: MetadataType; + action: 'edit' | 'add'; + import: boolean; + importMetadata: boolean; +}; + +export const useMetadataStore = defineStore({ + id: 'metadata', + state: () => ({ + model: { + item: undefined, + edit: false, + type: 'events', + action: 'add', + import: false, + importMetadata: false, + } as MetadataModelType + }), + actions: { + set(key: string, value: any) { + this.model[key] = value + } + } +}) \ No newline at end of file diff --git a/src/utils/comm.ts b/src/utils/comm.ts index a2bb3385..3c032a79 100644 --- a/src/utils/comm.ts +++ b/src/utils/comm.ts @@ -6,7 +6,7 @@ import { Terms } from 'components/Search/types' * @param path {String} 路径 */ export const getImage = (path: string) => { - return new URL('/images'+path, import.meta.url).href + return new URL('/images' + path, import.meta.url).href } export const LocalStore = { @@ -57,3 +57,30 @@ export const filterTreeSelectNode = (value: string, treeNode: any, key: string = export const filterSelectNode = (value: string, option: any, key: string = 'label'): boolean => { return option[key]?.includes(value) } + +/** + * 时间转换为'2022-01-02 14:03:05' + * @param date 时间对象 + * @returns + */ +export const dateFormat = (dateSouce:any):string|Error => { + let date = null + try { + date = new Date(dateSouce) + } catch (error) { + return new Error('请传入日期格式数据') + } + let year = date.getFullYear(); + let month: number | string = date.getMonth() + 1; + let day: number | string = date.getDate(); + let hour: number | string = date.getHours(); + let minutes: number | string = date.getMinutes(); + let seconds: number | string = date.getSeconds(); + month = (month < 10) ? '0' + month : month; + day = (day < 10) ? '0' + day : day; + hour = (hour < 10) ? '0' + hour : hour; + minutes = (minutes < 10) ? '0' + minutes : minutes; + seconds = (seconds < 10) ? '0' + seconds : seconds; + return year + "-" + month + "-" + day + + " " + hour + ":" + minutes + ":" + seconds; +} diff --git a/src/views/device/components/Metadata/Base/Edit/index.vue b/src/views/device/components/Metadata/Base/Edit/index.vue new file mode 100644 index 00000000..1f0f0de7 --- /dev/null +++ b/src/views/device/components/Metadata/Base/Edit/index.vue @@ -0,0 +1,119 @@ + + + \ No newline at end of file diff --git a/src/views/device/components/Metadata/Base/columns.ts b/src/views/device/components/Metadata/Base/columns.ts new file mode 100644 index 00000000..7c4f0f63 --- /dev/null +++ b/src/views/device/components/Metadata/Base/columns.ts @@ -0,0 +1,91 @@ +import { JColumnProps } from "@/components/Table"; + +const SourceMap = { + device: '设备', + manual: '手动', + rule: '规则', +}; + +const type = { + read: '读', + write: '写', + report: '上报', +}; + +const BaseColumns: JColumnProps[] = [ + { + title: '标识', + dataIndex: 'id', + ellipsis: true, + }, + { + title: '名称', + dataIndex: 'name', + ellipsis: true, + }, + { + title: '说明', + dataIndex: 'description', + ellipsis: true, + }, +]; + +const EventColumns: JColumnProps[] = BaseColumns.concat([ + { + title: '事件级别', + dataIndex: 'expands', + scopedSlots: true, + }, +]); + +const FunctionColumns: JColumnProps[] = BaseColumns.concat([ + { + title: '是否异步', + dataIndex: 'async', + scopedSlots: true, + }, + // { + // title: '读写类型', + // dataIndex: 'expands', + // render: (text: any) => (text?.type || []).map((item: string | number) => {type[item]}), + // }, +]); + +const PropertyColumns: JColumnProps[] = BaseColumns.concat([ + { + title: '数据类型', + dataIndex: 'valueType', + scopedSlots: true, + }, + { + title: '属性来源', + dataIndex: 'expands', + scopedSlots: true, + }, + { + title: '读写类型', + dataIndex: 'expands', + scopedSlots: true, + }, +]); + +const TagColumns: JColumnProps[] = BaseColumns.concat([ + { + title: '数据类型', + dataIndex: 'valueType', + scopedSlots: true, + }, + { + title: '读写类型', + dataIndex: 'expands', + scopedSlots: true, + }, +]); + +const MetadataMapping = new Map(); +MetadataMapping.set('properties', PropertyColumns); +MetadataMapping.set('events', EventColumns); +MetadataMapping.set('tags', TagColumns); +MetadataMapping.set('functions', FunctionColumns); + +export default MetadataMapping; \ No newline at end of file diff --git a/src/views/device/components/Metadata/Base/index.vue b/src/views/device/components/Metadata/Base/index.vue new file mode 100644 index 00000000..35e9dfed --- /dev/null +++ b/src/views/device/components/Metadata/Base/index.vue @@ -0,0 +1,100 @@ + + + \ No newline at end of file diff --git a/src/views/device/components/Metadata/Import/index.vue b/src/views/device/components/Metadata/Import/index.vue index eda57092..fb6028bd 100644 --- a/src/views/device/components/Metadata/Import/index.vue +++ b/src/views/device/components/Metadata/Import/index.vue @@ -47,14 +47,16 @@ import { saveMetadata } from '@/api/device/instance' import { queryNoPagingPost, convertMetadata, modify } from '@/api/device/product' import type { DefaultOptionType } from 'ant-design-vue/es/select'; import { UploadProps } from 'ant-design-vue/es'; -import type { DeviceMetadata } from '@/views/device/Product/typings' +import type { DeviceMetadata, ProductItem } from '@/views/device/Product/typings' import { message } from 'ant-design-vue/es'; import { Store } from 'jetlinks-store'; import { SystemConst } from '@/utils/consts'; import { useInstanceStore } from '@/store/instance' +import { useProductStore } from '@/store/product'; const route = useRoute() const instanceStore = useInstanceStore() +const productStore = useProductStore() interface Props { visible: boolean, @@ -191,8 +193,10 @@ const handleImport = async () => { const { id } = route.params || {} if (props?.type === 'device') { await saveMetadata(id as string, metadata) + instanceStore.setCurrent(JSON.parse(metadata || '{}')) } else { await modify(id as string, { metadata: metadata }) + productStore.setCurrent(JSON.parse(metadata || '{}')) } loading.value = false // MetadataAction.insert(JSON.parse(metadata || '{}')); @@ -231,10 +235,12 @@ const handleImport = async () => { if (props?.type === 'device') { const metadata: DeviceMetadata = JSON.parse(paramsDevice || '{}') // MetadataAction.insert(metadata); + instanceStore.setCurrent(metadata) message.success('导入成功') } else { - const metadata: DeviceMetadata = JSON.parse(params?.metadata || '{}') + const metadata: ProductItem = JSON.parse(params?.metadata || '{}') // MetadataAction.insert(metadata); + productStore.setCurrent(metadata) message.success('导入成功') } } diff --git a/src/views/device/components/Metadata/metadata.ts b/src/views/device/components/Metadata/metadata.ts new file mode 100644 index 00000000..134a67c0 --- /dev/null +++ b/src/views/device/components/Metadata/metadata.ts @@ -0,0 +1,61 @@ +import { saveProductMetadata } from "@/api/device/product"; +import { saveMetadata } from "@/api/device/instance"; +import type { DeviceInstance } from "../../Instance/typings"; +import type { DeviceMetadata, MetadataItem, MetadataType, ProductItem } from "../../Product/typings"; + +/** + * 更新物模型 + * @param type 物模型类型 events + * @param item 物模型数据 【{a},{b},{c}】 + // * @param target product、device + * @param data product 、device [{event:[1,2,3]] + * @param onEvent 数据更新回调:更新数据库、发送事件等操作 + * + */ + export const updateMetadata = ( + type: MetadataType, + item: MetadataItem[], + // target: 'product' | 'device', + data: ProductItem | DeviceInstance, + onEvent?: (item: string) => void, +): ProductItem | DeviceInstance => { + if (!data) return data; + const metadata = JSON.parse(data.metadata || '{}') as DeviceMetadata; + const config = (metadata[type] || []) as MetadataItem[]; + if (item.length > 0) { + item.forEach((i) => { + const index = config.findIndex((c) => c.id === i.id); + if (index > -1) { + config[index] = i; + // onEvent?.('update', i); + } else { + config.push(i); + // onEvent?.('add', i); + } + }); + } else { + console.warn('未触发物模型修改'); + } + // @ts-ignore + metadata[type] = config.sort((a, b) => b?.sortsIndex - a?.sortsIndex); + data.metadata = JSON.stringify(metadata); + onEvent?.(data.metadata) + return data; +}; + +/** + * 保存物模型数据到服务器 + * @param type 类型 + * @param data 数据 + */ +export const asyncUpdateMetadata = ( + type: 'product' | 'device', + data: ProductItem | DeviceInstance, +): Promise => { + switch (type) { + case 'product': + return saveProductMetadata(data); + case 'device': + return saveMetadata(data.id, JSON.parse(data.metadata || '{}')); + } +}; \ No newline at end of file diff --git a/src/views/home/components/StatusLabel.vue b/src/views/home/components/StatusLabel.vue new file mode 100644 index 00000000..59a720f6 --- /dev/null +++ b/src/views/home/components/StatusLabel.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/src/views/home/components/StepCard.vue b/src/views/home/components/StepCard.vue index ccc87a5d..661de226 100644 --- a/src/views/home/components/StepCard.vue +++ b/src/views/home/components/StepCard.vue @@ -21,11 +21,11 @@
- - @@ -38,8 +38,8 @@ import { PropType } from 'vue'; import { QuestionCircleOutlined } from '@ant-design/icons-vue'; import { message } from 'ant-design-vue'; -import AccessMethodDialog from './dialogs/AccessMethodDialog.vue'; -import FuncTestDialog from './dialogs/FuncTestDialog.vue'; +import ProductChooseDialog from './dialogs/ProductChooseDialog.vue'; +import DeviceChooseDialog from './dialogs/DeviceChooseDialog.vue'; import { recommendList } from '../index'; @@ -73,9 +73,8 @@ const jumpPage = (row: recommendList) => { } }; // 弹窗返回后的二次跳转 -const againJumpPage = (paramsSource: object) => { - const params = { ...(selectRow.params || {}), ...paramsSource }; - router.push(`${selectRow.linkUrl}${objToParams(params || {})}`); +const againJumpPage = (params: string) => { + router.push(`${selectRow.linkUrl}/${params}`); }; const objToParams = (source: object): string => { diff --git a/src/views/home/components/dialogs/DeviceChooseDialog.vue b/src/views/home/components/dialogs/DeviceChooseDialog.vue new file mode 100644 index 00000000..18a03947 --- /dev/null +++ b/src/views/home/components/dialogs/DeviceChooseDialog.vue @@ -0,0 +1,181 @@ + + + + + diff --git a/src/views/home/components/dialogs/FuncTestDialog.vue b/src/views/home/components/dialogs/FuncTestDialog.vue deleted file mode 100644 index f04db868..00000000 --- a/src/views/home/components/dialogs/FuncTestDialog.vue +++ /dev/null @@ -1,98 +0,0 @@ - - - - - diff --git a/src/views/home/components/dialogs/AccessMethodDialog.vue b/src/views/home/components/dialogs/ProductChooseDialog.vue similarity index 96% rename from src/views/home/components/dialogs/AccessMethodDialog.vue rename to src/views/home/components/dialogs/ProductChooseDialog.vue index 6bf83d48..07cb6e95 100644 --- a/src/views/home/components/dialogs/AccessMethodDialog.vue +++ b/src/views/home/components/dialogs/ProductChooseDialog.vue @@ -53,7 +53,7 @@ const productList = ref<[productItem] | []>([]); const getContainer = () => proxy?.$refs.modal as HTMLElement; const getOptions = () => { - getProductList_api().then((resp) => { + getProductList_api().then((resp:any) => { productList.value = resp.result .filter((i: any) => !i?.accessId) .map((item: { name: any; id: any }) => ({ @@ -63,7 +63,7 @@ const getOptions = () => { }); }; const handleOk = () => { - emits('confirm', form.value); + emits('confirm', form.value.productId); visible.value = false; }; const filterOption = (input: string, option: any) => { diff --git a/src/views/home/index.d.ts b/src/views/home/index.d.ts index 6b8a22bb..19b3c366 100644 --- a/src/views/home/index.d.ts +++ b/src/views/home/index.d.ts @@ -8,7 +8,11 @@ export interface recommendList { auth: boolean; dialogTag?: 'accessMethod' | 'funcTest'; } - +// 产品列表里的每项 +export interface productItem { + label: string; + value: string +} export interface deviceInfo { deviceId: string, deviceName: string, diff --git a/src/views/home/modules/config.ts b/src/views/home/modules/config.ts index 5857b8d0..377db512 100644 --- a/src/views/home/modules/config.ts +++ b/src/views/home/modules/config.ts @@ -1,9 +1,10 @@ // import {getImage} from '@/utils/comm' +import { useMenuStore } from "@/store/menu"; import { usePermissionStore } from "@/store/permission"; import { recommendList, bootConfig } from "../index"; -// 权限控制 +// 按钮权限控制 const hasPermission = usePermissionStore().hasPermission; const productPermission = (action: string) => hasPermission(`device/Product:${action}`); @@ -11,6 +12,8 @@ const devicePermission = (action: string) => hasPermission(`device/Instance:${action}`); const rulePermission = (action: string) => hasPermission(`rule-engine/Instance:${action}`); +// 页面权限控制 +const menuPermission = useMenuStore().hasPermission // 物联网引导-数据 @@ -18,7 +21,7 @@ export const deviceBootConfig: bootConfig[] = [ { english: 'STEP1', label: '创建产品', - link: '/a', + link: '/iot/device/Product', auth: productPermission('add'), params: { save: true, @@ -27,7 +30,7 @@ export const deviceBootConfig: bootConfig[] = [ { english: 'STEP2', label: '创建设备', - link: '/b', + link: '/iot/device/Instance', auth: devicePermission('add'), params: { save: true, @@ -36,7 +39,7 @@ export const deviceBootConfig: bootConfig[] = [ { english: 'STEP3', label: '规则引擎', - link: '/c', + link: '/iot/rule-engine/Instance', auth: rulePermission('add'), params: { save: true, @@ -50,7 +53,7 @@ export const deviceStepDetails: recommendList[] = [ details: '产品是设备的集合,通常指一组具有相同功能的设备。物联设备必须通过产品进行接入方式配置。', iconUrl: '/images/home/bottom-4.png', - linkUrl: '/a', + linkUrl: '/iot/device/Product', auth: productPermission('add'), params: { save: true, @@ -61,7 +64,7 @@ export const deviceStepDetails: recommendList[] = [ details: '通过产品对同一类型的设备进行统一的接入方式配置。请参照设备铭牌说明选择匹配的接入方式。', iconUrl: '/images/home/bottom-1.png', - linkUrl: '/a', + linkUrl: '/iot/device/Product/detail', auth: productPermission('update'), dialogTag: 'accessMethod', }, @@ -69,7 +72,7 @@ export const deviceStepDetails: recommendList[] = [ title: '添加测试设备', details: '添加单个设备,用于验证产品模型是否配置正确。', iconUrl: '/images/home/bottom-5.png', - linkUrl: '/a', + linkUrl: '/iot/device/Instance', auth: devicePermission('add'), params: { save: true, @@ -80,15 +83,16 @@ export const deviceStepDetails: recommendList[] = [ details: '对添加的测试设备进行功能调试,验证能否连接到平台,设备功能是否配置正确。', iconUrl: '/images/home/bottom-2.png', - linkUrl: '/a', - auth: devicePermission('update'), + linkUrl: '/iot/device/Instance/detail', + // auth: devicePermission('update'), + auth: true, dialogTag: 'funcTest', }, { title: '批量添加设备', details: '批量添加同一产品下的设备', iconUrl: '/images/home/bottom-3.png', - linkUrl: '/a', + linkUrl: '/iot/device/Instance', auth: devicePermission('import'), params: { import: true, @@ -102,14 +106,14 @@ export const opsBootConfig: bootConfig[] = [ { english: 'STEP1', label: '设备接入配置', - link: '/a', - auth: true, + link: '/iot/link/accessConfig', + auth: menuPermission('link/accessConfig'), }, { english: 'STEP2', label: '日志排查', - link: '/b', - auth: true, + link: '/iot/link/Log', + auth: menuPermission('link/Log'), params: { key: 'system', }, @@ -117,8 +121,8 @@ export const opsBootConfig: bootConfig[] = [ { english: 'STEP3', label: '实时监控', - link: '/c', - auth: false, + link: '/iot/link/dashboard', + auth: menuPermission('link/dashboard'), params: { save: true, }, @@ -131,44 +135,38 @@ export const opsStepDetails: recommendList[] = [ details: '根据业务需求自定义开发对应的产品(设备模型)接入协议,并上传到平台。', iconUrl: '/images/home/bottom-1.png', - linkUrl: '/a', - auth: true, - params: { - a: 1, - save: true, - }, + linkUrl: '/iot/link/protocol', + auth: menuPermission('link/Protocol'), + }, { title: '证书管理', details: '统一维护平台内的证书,用于数据通信加密。', iconUrl: '/images/home/bottom-6.png', - linkUrl: '/a', - auth: true, - params: { - a: 1, - save: false, - }, + linkUrl: '/iot/link/Certificate', + auth: menuPermission('link/Certificate'), + }, { title: '网络组件', details: '根据不同的传输类型配置平台底层网络组件相关参数。', iconUrl: '/images/home/bottom-3.png', - linkUrl: '/a', - auth: true, + linkUrl: '/iot/link/type', + auth: menuPermission('link/Type'), }, { title: '设备接入网关', details: '根据不同的传输类型,关联消息协议,配置设备接入网关相关参数。', iconUrl: '/images/home/bottom-4.png', - linkUrl: '/a', - auth: true, + linkUrl: '/iot/link/accessConfig', + auth: menuPermission('link/AccessConfig'), }, { title: '日志管理', details: '监控系统日志,及时处理系统异常。', iconUrl: '/images/home/bottom-5.png', - linkUrl: '/a', - auth: false, + linkUrl: '/iot/link/Log', + auth: menuPermission('Log'), params: { key: 'system', } diff --git a/src/views/system/Permission/components/EditDialog.vue b/src/views/system/Permission/components/EditDialog.vue index d78ac2bb..953a084d 100644 --- a/src/views/system/Permission/components/EditDialog.vue +++ b/src/views/system/Permission/components/EditDialog.vue @@ -9,7 +9,10 @@ -
+
+ + {{ + i + 1 + }} + - - {{ - i + 1 - }} -
@@ -105,27 +109,40 @@ import { FormInstance, message } from 'ant-design-vue'; import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue'; import { QuestionCircleOutlined } from '@ant-design/icons-vue'; +import { Rule } from 'ant-design-vue/es/form'; + +import { + checkId_api, + editPermission_api, + addPermission_api, +} from '@/api/system/permission'; const defaultAction = [ { action: 'query', name: '查询', describe: '查询' }, { action: 'save', name: '保存', describe: '保存' }, { action: 'delete', name: '删除', describe: '删除' }, ]; +const emits = defineEmits(['refresh']); // 弹窗相关 const dialog = reactive({ title: '', visible: false, handleOk: () => { - formRef.value?.validate().then(() => console.log('success')); + formRef.value?.validate().then(() => { + form.submit(); + }); }, // 控制弹窗的打开与关闭 changeVisible: (status: boolean, defaultForm: any = {}) => { - form.data = { name: '', description: '', ...defaultForm }; dialog.title = defaultForm.id ? '编辑' : '新增'; + form.data = { name: '', ...defaultForm }; table.data = defaultForm.id ? defaultForm.actions : [...defaultAction]; pager.total = table.data.length; pager.current = 1; dialog.visible = status; + nextTick(() => { + formRef.value?.clearValidate(); + }); }, }); // 表单相关 @@ -136,6 +153,46 @@ const form = reactive({ name: '', id: '', }, + rules: { + // 校验标识是否可用 + idCheck: (_rule: Rule, id: string, cb: Function) => { + if (!id) return cb('请输入标识(ID)'); + if (dialog.title === '编辑') return cb(); + checkId_api({ id }) + .then((resp: any) => { + if (resp.status === 200 && !resp.result.passed) + cb(resp.result.reason); + else cb(); + }) + .catch(() => cb('验证失败')); + + // return new Promise((resolve) => { + // checkId_api({ id }) + // .then((resp: any) => { + // if (resp.status === 200 && !resp.result.passed) + // resolve(resp.result.reason); + // else resolve(''); + // }) + // .catch(() => resolve('验证失败')); + // }); + }, + }, + submit: () => { + const params = { + ...form.data, + actions: table.data.filter((item: any) => item.action && item.name), + }; + const api = + dialog.title === '编辑' ? editPermission_api : addPermission_api; + + api(params).then((resp) => { + if (resp.status === 200) { + message.error('操作成功'); + emits('refresh'); + dialog.visible = false; + } + }); + }, }); const table = reactive({ @@ -144,21 +201,26 @@ const table = reactive({ title: '-', dataIndex: 'index', key: 'index', + width:80, + align:'center' }, { title: '操作类型', dataIndex: 'action', key: 'action', + width: 220 }, { title: '名称', dataIndex: 'name', key: 'name', + width: 220 }, { title: '说明', dataIndex: 'describe', key: 'describe', + width: 220 }, { title: '操作', @@ -230,5 +292,30 @@ defineExpose({ } } } + + .ant-table { + color: #ff4d4f; + + .ant-table-tbody { + color: #ff4d4f; + } + } + .delete-btn { + color: #000000d9; + &:hover{ + color: #415ed1; + } + } + .pager { + display: flex; + justify-content: center; + margin-bottom: 12px; + .ant-pagination { + margin-left: 8px; + :deep(.ant-pagination-item) { + display: none; + } + } + } } diff --git a/src/views/system/Permission/index.vue b/src/views/system/Permission/index.vue index ec763acd..d221cc98 100644 --- a/src/views/system/Permission/index.vue +++ b/src/views/system/Permission/index.vue @@ -1,6 +1,6 @@